From d8fbd51e93626d13cd4c42975a1b76993d3bc591 Mon Sep 17 00:00:00 2001 From: "Misty (Bot)" Date: Fri, 12 Jan 2018 09:25:24 +0000 Subject: [PATCH 01/32] Latest translations and fallbacks --- public/language/tr/email.json | 2 +- public/language/tr/pages.json | 4 ++-- public/language/tr/user.json | 16 ++++++++-------- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/public/language/tr/email.json b/public/language/tr/email.json index 71077d5cc7..dcb4e12250 100644 --- a/public/language/tr/email.json +++ b/public/language/tr/email.json @@ -30,7 +30,7 @@ "notif.chat.unsub.info": "Bu bildirim şectiğiniz ayarlar yüzünden gönderildi.", "notif.post.cta": "Konunun tamamını okumak için buraya tıklayın", "notif.post.unsub.info": "Bu yazı bildirimi size abonelik ayarlarınız nedeni ile gönderilmiştir.", - "notif.cta": "Click here to go to forum", + "notif.cta": "Foruma gitmek için buraya tıklayın", "test.text1": "Bu ileti NodeBB e-posta ayarlarınızın doğru çalışıp çalışmadığını kontrol etmek için gönderildi.", "unsub.cta": "Buraya tıklayarak ayarlarınızı değiştirebilirsiniz.", "banned.subject": "%1 'den yasaklandınız", diff --git a/public/language/tr/pages.json b/public/language/tr/pages.json index 175e6c5a45..5056f72773 100644 --- a/public/language/tr/pages.json +++ b/public/language/tr/pages.json @@ -6,7 +6,7 @@ "popular-month": "Bu ayki popüler başlıklar", "popular-alltime": "En popüler başlıklar", "recent": "Güncel Konular", - "top": "Top Voted Topics", + "top": "En Çok Oylanan Başlıklar", "moderator-tools": "Moderatör Araçları", "flagged-content": "Bayraklanan İçerik", "ip-blacklist": "IP Kara Listesi", @@ -45,7 +45,7 @@ "account/bookmarks": "%1'in yer imine eklenmiş iletiler", "account/settings": "Kullanıcı Ayarları", "account/watched": "%1 tarafından izlenen konular", - "account/ignored": "Topics ignored by %1", + "account/ignored": "%1 tarafından konu yok sayıldı", "account/upvoted": "%1 tarafından artılanan gönderiler", "account/downvoted": "%1 tarafından eksilenen gönderiler", "account/best": "%1 tarafından en iyi gönderiler", diff --git a/public/language/tr/user.json b/public/language/tr/user.json index 6a0e1c2685..477dd4d27e 100644 --- a/public/language/tr/user.json +++ b/public/language/tr/user.json @@ -85,7 +85,7 @@ "has_no_posts": "Bu kullanıcı henüz herhangi bir ileti yazmamış.", "has_no_topics": "Bu kullanıcı henüz hiç bir başlık açmamış.", "has_no_watched_topics": "Bu kullanıcı henüz hiç bir başlık okumamış.", - "has_no_ignored_topics": "This user hasn't ignored any topics yet.", + "has_no_ignored_topics": "Bu kullanıcı henüz hiçbir başlığı yok saymadı.", "has_no_upvoted_posts": "Bu kullanıcı henüz hiç bir gönderiyi artılamamış.", "has_no_downvoted_posts": "Bu kullanıcı henüz hiç bir gönderiyi eksilememiş.", "has_no_voted_posts": "Bu kullanıcının hiç oylanmış gönderisi yok.", @@ -101,11 +101,11 @@ "outgoing-message-sound": "Giden ileti sesi", "notification-sound": "Bildirim sesi", "no-sound": "Ses yok", - "upvote-notif-freq": "Upvote Notification Frequency", + "upvote-notif-freq": "Artı Oy Bildiri Sıklığı", "upvote-notif-freq.all": "Büyün Artı Oylar", - "upvote-notif-freq.everyTen": "Every Ten Upvotes", - "upvote-notif-freq.logarithmic": "On 10, 100, 1000...", - "upvote-notif-freq.disabled": "Disabled", + "upvote-notif-freq.everyTen": "Her Artı On Oy", + "upvote-notif-freq.logarithmic": "10, 100, 1000...", + "upvote-notif-freq.disabled": "Devre dışı", "browsing": "Tarayıcı Ayaları", "open_links_in_new_tab": "Dışarı giden bağlantıları yeni sekmede aç", "enable_topic_searching": "Konu içi aramayı aktive et", @@ -126,9 +126,9 @@ "sso.title": "Tek giriş servisleri", "sso.associated": "Birleştirilmiş", "sso.not-associated": "Birleştirmek için buraya tıklayın", - "sso.dissociate": "Dissociate", - "sso.dissociate-confirm-title": "Confirm Dissociation", - "sso.dissociate-confirm": "Are you sure you wish to dissociate your account from %1?", + "sso.dissociate": "Ayrış", + "sso.dissociate-confirm-title": "Ayrışmayı Onayla", + "sso.dissociate-confirm": "%1 'den ayrışmak istediğinizden emin misiniz?", "info.latest-flags": "Son Bayraklar", "info.no-flags": "Hiç bayraklanan bir ileti bulunamadı", "info.ban-history": "Yasaklama Olayları", From 72140e5338fb9fe5218a2c17eaf46b4dbc27faec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Fri, 12 Jan 2018 17:29:47 -0500 Subject: [PATCH 02/32] closes #5991, closes #5884 --- install/data/defaults.json | 2 +- install/package.json | 4 +- .../en-GB/admin/settings/reputation.json | 5 +- public/language/en-GB/error.json | 3 + src/controllers/accounts/edit.js | 3 + src/flags.js | 4 +- src/posts/votes.js | 2 +- src/privileges/posts.js | 2 +- .../1.8.0/rename_min_reputation_settings.js | 25 +++++++ src/user/profile.js | 75 +++++++++++++++---- src/views/admin/settings/reputation.tpl | 7 +- test/controllers-admin.js | 12 +-- test/flags.js | 4 +- 13 files changed, 115 insertions(+), 33 deletions(-) create mode 100644 src/upgrades/1.8.0/rename_min_reputation_settings.js diff --git a/install/data/defaults.json b/install/data/defaults.json index 9e08416ce6..3d45ff6565 100644 --- a/install/data/defaults.json +++ b/install/data/defaults.json @@ -38,5 +38,5 @@ "bookmarkThreshold": 5, "topicsPerList": 20, "autoDetectLang": 1, - "privileges:flag": 0 + "min:rep:flag": 0 } diff --git a/install/package.json b/install/package.json index f3538ddbcf..2bbdcedca7 100644 --- a/install/package.json +++ b/install/package.json @@ -69,9 +69,9 @@ "nodebb-plugin-spam-be-gone": "0.5.1", "nodebb-rewards-essentials": "0.0.9", "nodebb-theme-lavender": "5.0.0", - "nodebb-theme-persona": "7.2.11", + "nodebb-theme-persona": "7.2.12", "nodebb-theme-slick": "1.1.2", - "nodebb-theme-vanilla": "8.1.5", + "nodebb-theme-vanilla": "8.1.6", "nodebb-widget-essentials": "4.0.1", "nodemailer": "4.4.1", "passport": "^0.4.0", diff --git a/public/language/en-GB/admin/settings/reputation.json b/public/language/en-GB/admin/settings/reputation.json index f0e59e8db9..c698592cff 100644 --- a/public/language/en-GB/admin/settings/reputation.json +++ b/public/language/en-GB/admin/settings/reputation.json @@ -5,5 +5,8 @@ "votes-are-public": "All Votes Are Public", "thresholds": "Activity Thresholds", "min-rep-downvote": "Minimum reputation to downvote posts", - "min-rep-flag": "Minimum reputation to flag posts" + "min-rep-flag": "Minimum reputation to flag posts", + "min-rep-website": "Minimum reputation to add \"Website\" to user profile", + "min-rep-aboutme": "Minimum reputation to add \"About me\" to user profile", + "min-rep-signature": "Minimum reputation to add \"Signature\" to user profile" } \ No newline at end of file diff --git a/public/language/en-GB/error.json b/public/language/en-GB/error.json index 7700006520..df48972483 100644 --- a/public/language/en-GB/error.json +++ b/public/language/en-GB/error.json @@ -146,6 +146,9 @@ "downvoting-disabled": "Downvoting is disabled", "not-enough-reputation-to-downvote": "You do not have enough reputation to downvote this post", "not-enough-reputation-to-flag": "You do not have enough reputation to flag this post", + "not-enough-reputation-min-rep-website": "You do not have enough reputation to add a website", + "not-enough-reputation-min-rep-aboutme": "You do not have enough reputation to add an about me", + "not-enough-reputation-min-rep-signature": "You do not have enough reputation to add a signature", "already-flagged": "You have already flagged this post", "self-vote": "You cannot vote on your own post", diff --git a/src/controllers/accounts/edit.js b/src/controllers/accounts/edit.js index b5bd4cf3a5..5e40b83ef2 100644 --- a/src/controllers/accounts/edit.js +++ b/src/controllers/accounts/edit.js @@ -28,6 +28,9 @@ editController.get = function (req, res, callback) { userData.maximumProfileImageSize = parseInt(meta.config.maximumProfileImageSize, 10); userData.allowProfileImageUploads = parseInt(meta.config.allowProfileImageUploads, 10) === 1; userData.allowAccountDelete = parseInt(meta.config.allowAccountDelete, 10) === 1; + userData.allowWebsite = !userData.isSelf || parseInt(userData.reputation, 10) >= (parseInt(meta.config['min:rep:website'], 10) || 0); + userData.allowAboutMe = !userData.isSelf || parseInt(userData.reputation, 10) >= (parseInt(meta.config['min:rep:aboutme'], 10) || 0); + userData.allowSignature = !userData.isSelf || parseInt(userData.reputation, 10) >= (parseInt(meta.config['min:rep:signature'], 10) || 0); userData.profileImageDimension = parseInt(meta.config.profileImageDimension, 10) || 200; userData.defaultAvatar = user.getDefaultAvatar(); diff --git a/src/flags.js b/src/flags.js index 4118cf8fb2..f906954318 100644 --- a/src/flags.js +++ b/src/flags.js @@ -241,7 +241,7 @@ Flags.validate = function (payload, callback) { return callback(err); } - var minimumReputation = utils.isNumber(meta.config['privileges:flag']) ? parseInt(meta.config['privileges:flag'], 10) : 0; + var minimumReputation = utils.isNumber(meta.config['min:rep:flag']) ? parseInt(meta.config['min:rep:flag'], 10) : 0; // Check if reporter meets rep threshold (or can edit the target post, in which case threshold does not apply) if (!editable.flag && parseInt(data.reporter.reputation, 10) < minimumReputation) { return callback(new Error('[[error:not-enough-reputation-to-flag]]')); @@ -257,7 +257,7 @@ Flags.validate = function (payload, callback) { return callback(err); } - var minimumReputation = utils.isNumber(meta.config['privileges:flag']) ? parseInt(meta.config['privileges:flag'], 10) : 0; + var minimumReputation = utils.isNumber(meta.config['min:rep:flag']) ? parseInt(meta.config['min:rep:flag'], 10) : 0; // Check if reporter meets rep threshold (or can edit the target user, in which case threshold does not apply) if (!editable && parseInt(data.reporter.reputation, 10) < minimumReputation) { return callback(new Error('[[error:not-enough-reputation-to-flag]]')); diff --git a/src/posts/votes.js b/src/posts/votes.js index 529fcbd0c3..422ca67efa 100644 --- a/src/posts/votes.js +++ b/src/posts/votes.js @@ -179,7 +179,7 @@ module.exports = function (Posts) { return callback(new Error('[[error:self-vote]]')); } - if (command === 'downvote' && parseInt(results.reputation, 10) < parseInt(meta.config['privileges:downvote'], 10)) { + if (command === 'downvote' && parseInt(results.reputation, 10) < parseInt(meta.config['min:rep:downvote'], 10)) { return callback(new Error('[[error:not-enough-reputation-to-downvote]]')); } diff --git a/src/privileges/posts.js b/src/privileges/posts.js index b157fa798b..1741dfa587 100644 --- a/src/privileges/posts.js +++ b/src/privileges/posts.js @@ -200,7 +200,7 @@ module.exports = function (privileges) { }, next); }, function (results, next) { - var minimumReputation = utils.isNumber(meta.config['privileges:flag']) ? parseInt(meta.config['privileges:flag'], 10) : 0; + var minimumReputation = utils.isNumber(meta.config['min:rep:flag']) ? parseInt(meta.config['min:rep:flag'], 10) : 0; var canFlag = results.isAdminOrMod || parseInt(results.userReputation, 10) >= minimumReputation; next(null, { flag: canFlag }); }, diff --git a/src/upgrades/1.8.0/rename_min_reputation_settings.js b/src/upgrades/1.8.0/rename_min_reputation_settings.js new file mode 100644 index 0000000000..1abbce1378 --- /dev/null +++ b/src/upgrades/1.8.0/rename_min_reputation_settings.js @@ -0,0 +1,25 @@ +'use strict'; + +var db = require('../../database'); + +module.exports = { + name: 'Rename privileges:downvote and privileges:flag to min:rep:downvote, min:rep:flag respectively', + timestamp: Date.UTC(2018, 0, 12), + method: function (callback) { + db.getObjectFields('config', ['privileges:downvote', 'privileges:flag'], function (err, config) { + if (err) { + return callback(err); + } + + db.setObject('config', { + 'min:rep:downvote': parseInt(config['privileges:downvote'], 10) || 0, + 'min:rep:flag': parseInt(config['privileges:downvote'], 10) || 0, + }, function (err) { + if (err) { + return callback(err); + } + db.deleteObjectFields('config', ['privileges:downvote', 'privileges:flag'], callback); + }); + }); + }, +}; diff --git a/src/user/profile.js b/src/user/profile.js index ae1e31c475..1af4821f49 100644 --- a/src/user/profile.js +++ b/src/user/profile.js @@ -17,14 +17,6 @@ module.exports = function (User) { var updateUid = data.uid; var oldData; - if (data.aboutme !== undefined && data.aboutme.length > meta.config.maximumAboutMeLength) { - return callback(new Error('[[error:about-me-too-long, ' + meta.config.maximumAboutMeLength + ']]')); - } - - if (data.signature !== undefined && data.signature.length > meta.config.maximumSignatureLength) { - return callback(new Error('[[error:signature-too-long, ' + meta.config.maximumSignatureLength + ']]')); - } - async.waterfall([ function (next) { plugins.fireHook('filter:user.updateProfile', { uid: uid, data: data, fields: fields }, next); @@ -33,13 +25,7 @@ module.exports = function (User) { fields = data.fields; data = data.data; - async.series([ - async.apply(isEmailAvailable, data, updateUid), - async.apply(isUsernameAvailable, data, updateUid), - async.apply(isGroupTitleValid, data), - ], function (err) { - next(err); - }); + validateData(uid, data, next); }, function (next) { User.getUserFields(updateUid, fields, next); @@ -73,6 +59,19 @@ module.exports = function (User) { ], callback); }; + function validateData(callerUid, data, callback) { + async.series([ + async.apply(isEmailAvailable, data, data.uid), + async.apply(isUsernameAvailable, data, data.uid), + async.apply(isGroupTitleValid, data), + async.apply(isWebsiteValid, callerUid, data), + async.apply(isAboutMeValid, callerUid, data), + async.apply(isSignatureValid, callerUid, data), + ], function (err) { + callback(err); + }); + } + function isEmailAvailable(data, uid, callback) { if (!data.email) { return callback(); @@ -141,6 +140,52 @@ module.exports = function (User) { } } + function isWebsiteValid(callerUid, data, callback) { + if (!data.website) { + return setImmediate(callback); + } + checkMinReputation(callerUid, data.uid, 'min:rep:website', callback); + } + + function isAboutMeValid(callerUid, data, callback) { + if (!data.aboutme) { + return setImmediate(callback); + } + if (data.aboutme !== undefined && data.aboutme.length > meta.config.maximumAboutMeLength) { + return callback(new Error('[[error:about-me-too-long, ' + meta.config.maximumAboutMeLength + ']]')); + } + + checkMinReputation(callerUid, data.uid, 'min:rep:aboutme', callback); + } + + function isSignatureValid(callerUid, data, callback) { + if (!data.signature) { + return setImmediate(callback); + } + if (data.signature !== undefined && data.signature.length > meta.config.maximumSignatureLength) { + return callback(new Error('[[error:signature-too-long, ' + meta.config.maximumSignatureLength + ']]')); + } + checkMinReputation(callerUid, data.uid, 'min:rep:signature', callback); + } + + function checkMinReputation(callerUid, uid, setting, callback) { + var isSelf = parseInt(callerUid, 10) === parseInt(uid, 10); + if (!isSelf) { + return setImmediate(callback); + } + async.waterfall([ + function (next) { + User.getUserField(uid, 'reputation', next); + }, + function (reputation, next) { + if (parseInt(reputation, 10) < (parseInt(meta.config[setting], 10) || 0)) { + return next(new Error('[[error:not-enough-reputation-' + setting.replace(/:/g, '-') + ']]')); + } + next(); + }, + ], callback); + } + function updateEmail(uid, newEmail, callback) { async.waterfall([ function (next) { diff --git a/src/views/admin/settings/reputation.tpl b/src/views/admin/settings/reputation.tpl index aee71910d4..7843273d2d 100644 --- a/src/views/admin/settings/reputation.tpl +++ b/src/views/admin/settings/reputation.tpl @@ -32,8 +32,11 @@
[[admin/settings/reputation:thresholds]]
- [[admin/settings/reputation:min-rep-downvote]]

- [[admin/settings/reputation:min-rep-flag]]

+ [[admin/settings/reputation:min-rep-downvote]]

+ [[admin/settings/reputation:min-rep-flag]]

+ [[admin/settings/reputation:min-rep-website]]

+ [[admin/settings/reputation:min-rep-aboutme]]

+ [[admin/settings/reputation:min-rep-signature]]

diff --git a/test/controllers-admin.js b/test/controllers-admin.js index de746fcc74..a04167d2f0 100644 --- a/test/controllers-admin.js +++ b/test/controllers-admin.js @@ -591,21 +591,21 @@ describe('Admin Controllers', function () { it('should error with not enough reputation to flag', function (done) { var socketFlags = require('../src/socket.io/flags'); - var oldValue = meta.config['privileges:flag']; - meta.config['privileges:flag'] = 1000; + var oldValue = meta.config['min:rep:flag']; + meta.config['min:rep:flag'] = 1000; socketFlags.create({ uid: regularUid }, { id: pid, type: 'post', reason: 'spam' }, function (err) { assert.equal(err.message, '[[error:not-enough-reputation-to-flag]]'); - meta.config['privileges:flag'] = oldValue; + meta.config['min:rep:flag'] = oldValue; done(); }); }); it('should return flag details', function (done) { var socketFlags = require('../src/socket.io/flags'); - var oldValue = meta.config['privileges:flag']; - meta.config['privileges:flag'] = 0; + var oldValue = meta.config['min:rep:flag']; + meta.config['min:rep:flag'] = 0; socketFlags.create({ uid: regularUid }, { id: pid, type: 'post', reason: 'spam' }, function (err, data) { - meta.config['privileges:flag'] = oldValue; + meta.config['min:rep:flag'] = oldValue; assert.ifError(err); request(nconf.get('url') + '/api/flags/' + data.flagId, { jar: moderatorJar, json: true }, function (err, res, body) { assert.ifError(err); diff --git a/test/flags.js b/test/flags.js index bead3bed0c..6dbcd5e08c 100644 --- a/test/flags.js +++ b/test/flags.js @@ -364,7 +364,7 @@ describe('Flags', function () { }); it('should not pass validation if flag threshold is set and user rep does not meet it', function (done) { - Meta.configs.set('privileges:flag', '50', function (err) { + Meta.configs.set('min:rep:flag', '50', function (err) { assert.ifError(err); Flags.validate({ @@ -374,7 +374,7 @@ describe('Flags', function () { }, function (err) { assert.ok(err); assert.strictEqual('[[error:not-enough-reputation-to-flag]]', err.message); - Meta.configs.set('privileges:flag', 0, done); + Meta.configs.set('min:rep:flag', 0, done); }); }); }); From 772872fb030aa0127da25f55bff66f2b5979aa8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Sat, 13 Jan 2018 18:10:47 -0500 Subject: [PATCH 03/32] trigger action:windows.loaded on coldload if widgets are already rendered --- public/src/widgets.js | 1 + 1 file changed, 1 insertion(+) diff --git a/public/src/widgets.js b/public/src/widgets.js index badc55c892..51f6e0d2be 100644 --- a/public/src/widgets.js +++ b/public/src/widgets.js @@ -15,6 +15,7 @@ locations.forEach(function (location) { var area = $('#content [widget-area="' + location + '"]'); if (area.length) { + $(window).trigger('action:widgets.loaded', {}); return; } From 4dee5a6101a611d39e01dbf43af1d77cc921de0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Sat, 13 Jan 2018 18:18:41 -0500 Subject: [PATCH 04/32] delete expireAt field --- src/upgrades/1.7.3/key_value_schema_change.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/upgrades/1.7.3/key_value_schema_change.js b/src/upgrades/1.7.3/key_value_schema_change.js index 4e747f6846..637d2c534d 100644 --- a/src/upgrades/1.7.3/key_value_schema_change.js +++ b/src/upgrades/1.7.3/key_value_schema_change.js @@ -48,7 +48,7 @@ module.exports = { done = true; return next(); } - + delete item.expireAt; if (Object.keys(item).length === 3 && item.hasOwnProperty('_key') && item.hasOwnProperty('value')) { client.collection('objects').update({ _key: item._key }, { $rename: { value: 'data' } }, next); } else { From 21a8d53c50f8bc5db82a7a48817591f2d2de037c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Sat, 13 Jan 2018 18:24:40 -0500 Subject: [PATCH 05/32] up themes --- install/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install/package.json b/install/package.json index 2bbdcedca7..7a1ad9c601 100644 --- a/install/package.json +++ b/install/package.json @@ -69,9 +69,9 @@ "nodebb-plugin-spam-be-gone": "0.5.1", "nodebb-rewards-essentials": "0.0.9", "nodebb-theme-lavender": "5.0.0", - "nodebb-theme-persona": "7.2.12", + "nodebb-theme-persona": "7.2.13", "nodebb-theme-slick": "1.1.2", - "nodebb-theme-vanilla": "8.1.6", + "nodebb-theme-vanilla": "8.1.7", "nodebb-widget-essentials": "4.0.1", "nodemailer": "4.4.1", "passport": "^0.4.0", From 4da9368857706ed656e4d4964366e4c021dc6d71 Mon Sep 17 00:00:00 2001 From: "Misty (Bot)" Date: Sun, 14 Jan 2018 09:24:38 +0000 Subject: [PATCH 06/32] Latest translations and fallbacks --- .../language/de/admin/general/homepage.json | 2 +- public/language/de/admin/manage/users.json | 22 +++++++++---------- public/language/de/admin/menu.json | 2 +- public/language/de/admin/settings/chat.json | 4 ++-- public/language/de/admin/settings/post.json | 20 ++++++++--------- public/language/de/error.json | 6 ++--- public/language/de/global.json | 2 +- public/language/de/pages.json | 2 +- 8 files changed, 30 insertions(+), 30 deletions(-) diff --git a/public/language/de/admin/general/homepage.json b/public/language/de/admin/general/homepage.json index fe3bdac766..186e89ae62 100644 --- a/public/language/de/admin/general/homepage.json +++ b/public/language/de/admin/general/homepage.json @@ -4,5 +4,5 @@ "home-page-route": "Startseitenpfad", "custom-route": "Eigener Startseitenpfad", "allow-user-home-pages": "Benutzern eigene Startseiten erlauben", - "home-page-title": "Title of the home page (default \"Home\")" + "home-page-title": "Titel der Startseite (Standardmäßig \"Home\")" } \ No newline at end of file diff --git a/public/language/de/admin/manage/users.json b/public/language/de/admin/manage/users.json index 58990723c8..e4857f610d 100644 --- a/public/language/de/admin/manage/users.json +++ b/public/language/de/admin/manage/users.json @@ -27,8 +27,8 @@ "pills.banned": "Gebannt", "pills.search": "Benutzer Suche", - "search.uid": "By User ID", - "search.uid-placeholder": "Enter a user ID to search", + "search.uid": "Nach Benutzer-ID", + "search.uid-placeholder": "Gib eine Benutzer-ID ein um danach zu suchen", "search.username": "Nach Nutzernamen", "search.username-placeholder": "Einen Nutzernamen eingeben, um danach zu suchen", "search.email": "Nach E-Mail", @@ -71,15 +71,15 @@ "alerts.lockout-reset-success": "Ausschlüsse zurückgesetzt", "alerts.flag-reset-success": "Meldung(en) zurückgesetzt!", "alerts.no-remove-yourself-admin": "Du kannst dich nicht selbst als Administrator degradieren!", - "alerts.make-admin-success": "User is now administrator.", - "alerts.confirm-remove-admin": "Do you really want to remove this administrator?", - "alerts.remove-admin-success": "User is no longer administrator.", - "alerts.make-global-mod-success": "User is now global moderator.", - "alerts.confirm-remove-global-mod": "Do you really want to remove this global moderator?", - "alerts.remove-global-mod-success": "User is no longer global noderator", - "alerts.make-moderator-success": "User is now moderator.", - "alerts.confirm-remove-moderator": "Do you really want to remove this moderator?", - "alerts.remove-moderator-success": "User is no longer moderator.", + "alerts.make-admin-success": "Der Benutzer ist nun ein Administrator", + "alerts.confirm-remove-admin": "Willst du wirklich diesen Administrator entfernen?", + "alerts.remove-admin-success": "Der Benutzer ist kein Administrator mehr", + "alerts.make-global-mod-success": "Der Benutzer ist nun ein globaler Moderator", + "alerts.confirm-remove-global-mod": "Willst du wirklich diesen globalen Moderator entfernen?", + "alerts.remove-global-mod-success": "Der Benutzer ist kein globaler Moderator mehr", + "alerts.make-moderator-success": "Der Benutzer ist nun ein Moderator", + "alerts.confirm-remove-moderator": "Willst du wirklich diesen Moderator entfernen?", + "alerts.remove-moderator-success": "Der Benutzer ist kein Moderator mehr", "alerts.confirm-validate-email": "Möchtest Du wirklich die E-Mails dieser Benutzer/dieses Benutzers bestätigen?", "alerts.validate-email-success": "E-Mails bestätigt", "alerts.password-reset-confirm": "Möchtest Du wirklich (eine) Passwort-Reset-Email(s) an diese(n) Benutzer schicken?", diff --git a/public/language/de/admin/menu.json b/public/language/de/admin/menu.json index af15dfed0d..748b72092a 100644 --- a/public/language/de/admin/menu.json +++ b/public/language/de/admin/menu.json @@ -9,7 +9,7 @@ "section-manage": "Verwalten", "manage/categories": "Kategorien", - "manage/privileges": "Privileges", + "manage/privileges": "Privilegien", "manage/tags": "Tags", "manage/users": "Benutzer", "manage/admins-mods": "Admins & Mods", diff --git a/public/language/de/admin/settings/chat.json b/public/language/de/admin/settings/chat.json index 8c7acb76e6..d80d8fb788 100644 --- a/public/language/de/admin/settings/chat.json +++ b/public/language/de/admin/settings/chat.json @@ -6,6 +6,6 @@ "max-length": "Maximale Chatnachrichtenlänge", "max-room-size": "Maximale Anzahl an Nutzern pro Chat-Room", "delay": "Zeit zwischen Chatnachrichten in Millisekunden", - "restrictions.seconds-edit-after": "Number of seconds before users are allowed to edit chat messages after posting. (0 disabled)", - "restrictions.seconds-delete-after": "Number of seconds before users are allowed to delete chat messages after posting. (0 disabled)" + "restrictions.seconds-edit-after": "Zeit in Sekunden bevor Benutzer Chat-Nachrichten editieren dürfen, nachdem sie erstellt wurden. (0 bedeutet deaktiviert)", + "restrictions.seconds-delete-after": "Zeit in Sekunden bevor Benutzer Chat-Nachrichten löschen dürfen, nachdem sie erstellt wurden. (0 bedeutet deaktiviert)" } \ No newline at end of file diff --git a/public/language/de/admin/settings/post.json b/public/language/de/admin/settings/post.json index ebfa7b0001..d43fe27f77 100644 --- a/public/language/de/admin/settings/post.json +++ b/public/language/de/admin/settings/post.json @@ -6,25 +6,25 @@ "sorting.most-votes": "Meiste Bewertungen", "sorting.most-posts": "Meiste Beiträge", "sorting.topic-default": "Standardmäßige Themensortierung", - "length": "Post Length", + "length": "Beitragslänge", "restrictions": "Posting beschränkungen", - "restrictions-new": "New User Restrictions", + "restrictions-new": "Beschränkungen für neue Benutzer", "restrictions.post-queue": "Beitragswarteschlange verwenden", - "restrictions-new.post-queue": "Enable new user restrictions", + "restrictions-new.post-queue": "Aktiviere Beschränkungen für neue Benutzer", "restrictions.post-queue-help": "Das verwenden der Beitragswarteschlange wird Beiträge von neuen Benutzern in eine Warteschlange zur Genehmigung setzen.", - "restrictions-new.post-queue-help": "Enabling new user restrictions will set restrictions on posts created by new users.", - "restrictions.seconds-between": "Seconds between posts", - "restrictions.seconds-between-new": "Seconds between posts for new users", - "restrictions.rep-threshold": "Reputation threshold before these restrictions are lifted", + "restrictions-new.post-queue-help": "Das aktivieren von Beschränkungen für neue Benutzer wird von neuen Benutzern erstelltw Beiträge beschränken.", + "restrictions.seconds-between": "Sekunden zwischen Beiträgen", + "restrictions.seconds-between-new": "Sekunden zwischen Beiträgen für neue Benutzer", + "restrictions.rep-threshold": "Mindesreputation bevor die Beschränkungen aufgehoben werden", "restrictions.seconds-defore-new": "Sekunden befor ein neuer Nutzer einen Beitrag erstellen kann", - "restrictions.seconds-edit-after": "Number of seconds before users are allowed to edit posts after posting. (0 disabled)", - "restrictions.seconds-delete-after": "Number of seconds before users are allowed to delete posts after posting. (0 disabled)", + "restrictions.seconds-edit-after": "Zeit in Sekunden bevor Benutzer ihre Beiträge editieren dürfen, nachdem sie erstellt wurden. (0 bedeutet deaktiviert)", + "restrictions.seconds-delete-after": "Zeit in Sekunden bevor Benutzer ihre Beiträge löschen dürfen, nachdem sie erstellt wurden. (0 bedeutet deaktiviert)", "restrictions.replies-no-delete": "Anzahl der Antworten auf einen Thema, die Benötigt werden um das löschen des Themas durch den Besitzer zu verhindern. (0 = deaktiviert)", "restrictions.min-title-length": "Minimale Titellänge", "restrictions.max-title-length": "Maximale Titellänge", "restrictions.min-post-length": "Minimale Beitragslänge", "restrictions.max-post-length": "Maximale Beitragslänge", - "restrictions.days-until-stale": "Days until topic is considered stale", + "restrictions.days-until-stale": "Tage bis ein Thema als alt angesehen wird", "restrictions.stale-help": "Wenn ein Thema als \"veraltet\" angesehen wird, wird Nutzern die versuchen diesem Thema zu antworten eine Warnung gezeigt", "timestamp": "Zeitstempel", "timestamp.cut-off": "Tageslimit für Relative Zeitangaben (in Tagen)", diff --git a/public/language/de/error.json b/public/language/de/error.json index 5686c49861..ddd24e8144 100644 --- a/public/language/de/error.json +++ b/public/language/de/error.json @@ -17,7 +17,7 @@ "invalid-login-credentials": "Ungültige Zugangsdaten", "invalid-username-or-password": "Bitte gib sowohl einen Benutzernamen als auch ein Passwort an", "invalid-search-term": "Ungültige Suchanfrage", - "invalid-url": "Invalid URL", + "invalid-url": "Ungültige URL", "csrf-invalid": "Dein Login war nicht erfolgreich da wahrscheinlich deine Sitzung abgelaufen ist. Bitte versuche es noch einmal", "invalid-pagination-value": "Ungültige Seitennummerierung, muss mindestens %1 und maximal %2 sein", "username-taken": "Der Benutzername ist bereits vergeben", @@ -114,8 +114,8 @@ "cant-edit-chat-message": "Du darfst diese Nachricht nicht ändern", "cant-remove-last-user": "Du kannst den letzten Benutzer nicht entfernen", "cant-delete-chat-message": "Du darfst diese Nachricht nicht löschen", - "chat-edit-duration-expired": "You are only allowed to edit chat messages for %1 second(s) after posting", - "chat-delete-duration-expired": "You are only allowed to delete chat messages for %1 second(s) after posting", + "chat-edit-duration-expired": "Du darfst Chat-Nachrichten nur bis zu %1 Sekunde(n) nach der erstellung verändern", + "chat-delete-duration-expired": "Du darfst Chat-Nachrichten nur bis zu %1 Sekunde(n) nach der erstellung löschen", "already-voting-for-this-post": "Du hast diesen Beitrag bereits bewertet.", "reputation-system-disabled": "Das Reputationssystem ist deaktiviert.", "downvoting-disabled": "Downvotes sind deaktiviert.", diff --git a/public/language/de/global.json b/public/language/de/global.json index b2e8905faf..e2c9e36742 100644 --- a/public/language/de/global.json +++ b/public/language/de/global.json @@ -53,7 +53,7 @@ "topics": "Themen", "posts": "Beiträge", "best": "Bestbewertet", - "votes": "Votes", + "votes": "Stimmen", "upvoters": "Upvoter", "upvoted": "Positiv bewertet", "downvoters": "Downvoter", diff --git a/public/language/de/pages.json b/public/language/de/pages.json index 042e854383..a8ddb4f7ae 100644 --- a/public/language/de/pages.json +++ b/public/language/de/pages.json @@ -6,7 +6,7 @@ "popular-month": "Beliebte Themen dieses Monats", "popular-alltime": "Beliebteste Themen", "recent": "Neueste Themen", - "top": "Top Voted Topics", + "top": "Bestbewertetste Themen", "moderator-tools": "Moderator-Werkzeuge", "flagged-content": "Gemeldeter Inhalt", "ip-blacklist": "IP Blacklist", From d5073a9a4f35f92247da4ef5cb8ba71b92f2226d Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Sun, 14 Jan 2018 16:49:09 -0500 Subject: [PATCH 07/32] better handle loading of flag details if getTarget returns null or undefined instead of {} for purged data Not quite sure in what scenario it would be null or undefined... --- src/controllers/mods.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/mods.js b/src/controllers/mods.js index a7cb252c2c..69bfd7fbbf 100644 --- a/src/controllers/mods.js +++ b/src/controllers/mods.js @@ -126,7 +126,7 @@ modsController.flags.detail = function (req, res, next) { assignees: results.assignees, type_bool: ['post', 'user', 'empty'].reduce(function (memo, cur) { if (cur !== 'empty') { - memo[cur] = results.flagData.type === cur && !!Object.keys(results.flagData.target).length; + memo[cur] = results.flagData.type === cur && (!results.flagData.target || !!Object.keys(results.flagData.target).length); } else { memo[cur] = !Object.keys(results.flagData.target).length; } From 0929fd2a113c54a416b5341a9a9aaddd227983a8 Mon Sep 17 00:00:00 2001 From: Peter Jaszkowiak Date: Sun, 14 Jan 2018 21:23:24 -0700 Subject: [PATCH 08/32] Fix issue where outdated packages are never updated --- src/cli/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cli/index.js b/src/cli/index.js index 07efaadf5e..933f5a0b1c 100644 --- a/src/cli/index.js +++ b/src/cli/index.js @@ -50,6 +50,7 @@ try { console.warn('Dependencies outdated or not yet installed.'); console.log('Installing them now...\n'); + packageInstall.updatePackageFile(); packageInstall.installAll(); require('colors'); From 9c1faa7643b421382d8c01875512e63c66191cc4 Mon Sep 17 00:00:00 2001 From: Peter Jaszkowiak Date: Sun, 14 Jan 2018 21:33:30 -0700 Subject: [PATCH 09/32] Fix running `./nodebb` with no arguments doing nothing --- src/cli/index.js | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/cli/index.js b/src/cli/index.js index 933f5a0b1c..76ceb5b071 100644 --- a/src/cli/index.js +++ b/src/cli/index.js @@ -293,16 +293,12 @@ program } }); -program - .command('*', {}, { - noHelp: true, - }) - .action(function () { - program.help(); - }); - require('./colors'); +if (process.argv.length === 2) { + program.help(); +} + program.executables = false; program.parse(process.argv); From ed9166b79601d9eb0a2a58e22a9b78dde3cb5307 Mon Sep 17 00:00:00 2001 From: Peter Jaszkowiak Date: Sun, 14 Jan 2018 23:26:35 -0700 Subject: [PATCH 10/32] Refactor template compilation Always use persona as a fallback for templates --- src/emailer.js | 10 +-- src/meta/templates.js | 177 +++++++++++++++++++----------------------- src/plugins.js | 3 + 3 files changed, 89 insertions(+), 101 deletions(-) diff --git a/src/emailer.js b/src/emailer.js index 10163cc512..a841eb1f91 100644 --- a/src/emailer.js +++ b/src/emailer.js @@ -10,6 +10,7 @@ var htmlToText = require('html-to-text'); var url = require('url'); var path = require('path'); var fs = require('fs'); +var _ = require('lodash'); var User = require('./user'); var Plugins = require('./plugins'); @@ -289,11 +290,10 @@ function buildCustomTemplates(config) { file.walk(viewsDir, next); }, function (paths, next) { - paths = paths.reduce(function (obj, p) { - var relative = path.relative(viewsDir, p); - obj['/' + relative] = p; - return obj; - }, {}); + paths = _.fromPairs(paths.map(function (p) { + var relative = path.relative(viewsDir, p).replace(/\\/g, '/'); + return [relative, p]; + })); meta.templates.processImports(paths, template.path, template.text, next); }, function (source, next) { diff --git a/src/meta/templates.js b/src/meta/templates.js index 943978ff56..f8b63d41fa 100644 --- a/src/meta/templates.js +++ b/src/meta/templates.js @@ -7,6 +7,7 @@ var async = require('async'); var path = require('path'); var fs = require('fs'); var nconf = require('nconf'); +var _ = require('lodash'); var plugins = require('../plugins'); var file = require('../file'); @@ -24,7 +25,7 @@ function processImports(paths, templatePath, source, callback) { return callback(null, source); } - var partial = '/' + matches[1]; + var partial = matches[1]; if (paths[partial] && templatePath !== partial) { fs.readFile(paths[partial], 'utf8', function (err, partialSource) { if (err) { @@ -43,124 +44,108 @@ function processImports(paths, templatePath, source, callback) { } Templates.processImports = processImports; -Templates.compile = function (callback) { - callback = callback || function () {}; +function getTemplateDirs(callback) { + var pluginTemplates = _.values(plugins.pluginsData) + .filter(function (pluginData) { + return !pluginData.id.startsWith('nodebb-theme-'); + }) + .map(function (pluginData) { + return path.join(__dirname, '../../node_modules/', pluginData.id, pluginData.templates || 'templates'); + }); var themeConfig = require(nconf.get('theme_config')); - var baseTemplatesPaths = themeConfig.baseTheme ? getBaseTemplates(themeConfig.baseTheme) : [nconf.get('base_templates_path')]; + var theme = themeConfig.baseTheme; + + var themePath; + var themeTemplates = [nconf.get('theme_templates_path')]; + while (theme) { + themePath = path.join(nconf.get('themes_path'), theme); + themeConfig = require(path.join(themePath, 'theme.json')); + + themeTemplates.push(path.join(themePath, themeConfig.templates || 'templates')); + theme = themeConfig.baseTheme; + } + + themeTemplates.push(nconf.get('base_templates_path')); + themeTemplates = _.uniq(themeTemplates.reverse()); + + var coreTemplatesPath = nconf.get('core_templates_path'); + + var templateDirs = _.uniq([coreTemplatesPath].concat(themeTemplates, pluginTemplates)); + + async.filter(templateDirs, file.exists, callback); +} + +function getTemplateFiles(dirs, callback) { + async.waterfall([ + function (cb) { + async.map(dirs, function (dir, next) { + file.walk(dir, function (err, files) { + if (err) { return next(err); } + + files = files.filter(function (path) { + return path.endsWith('.tpl'); + }).map(function (file) { + return { + name: path.relative(dir, file).replace(/\\/g, '/'), + path: file, + }; + }); + next(null, files); + }); + }, cb); + }, + function (buckets, cb) { + var dict = {}; + buckets.forEach(function (files) { + files.forEach(function (file) { + dict[file.name] = file.path; + }); + }); + + cb(null, dict); + }, + ], callback); +} + +function compile(callback) { + callback = callback || function () {}; async.waterfall([ function (next) { - preparePaths(baseTemplatesPaths, next); + rimraf(viewsPath, function (err) { next(err); }); + }, + function (next) { + mkdirp(viewsPath, function (err) { next(err); }); }, - function (paths, next) { - async.each(Object.keys(paths), function (relativePath, next) { + getTemplateDirs, + getTemplateFiles, + function (files, next) { + async.each(Object.keys(files), function (name, next) { + var filePath = files[name]; + async.waterfall([ function (next) { - fs.readFile(paths[relativePath], 'utf8', next); + fs.readFile(filePath, 'utf8', next); }, function (source, next) { - processImports(paths, relativePath, source, next); + processImports(files, name, source, next); }, function (source, next) { - mkdirp(path.join(viewsPath, path.dirname(relativePath)), function (err) { + mkdirp(path.join(viewsPath, path.dirname(name)), function (err) { next(err, source); }); }, function (compiled, next) { - fs.writeFile(path.join(viewsPath, relativePath), compiled, next); + fs.writeFile(path.join(viewsPath, name), compiled, next); }, ], next); }, next); }, - function (next) { - rimraf(path.join(viewsPath, '*.js'), next); - }, function (next) { winston.verbose('[meta/templates] Successfully compiled templates.'); next(); }, ], callback); -}; - -function getBaseTemplates(theme) { - var baseTemplatesPaths = []; - var baseThemePath; - var baseThemeConfig; - - while (theme) { - baseThemePath = path.join(nconf.get('themes_path'), theme); - baseThemeConfig = require(path.join(baseThemePath, 'theme.json')); - - baseTemplatesPaths.push(path.join(baseThemePath, baseThemeConfig.templates || 'templates')); - theme = baseThemeConfig.baseTheme; - } - - return baseTemplatesPaths.reverse(); -} - -function preparePaths(baseTemplatesPaths, callback) { - var coreTemplatesPath = nconf.get('core_templates_path'); - var pluginTemplates; - async.waterfall([ - function (next) { - rimraf(viewsPath, next); - }, - function (next) { - mkdirp(viewsPath, next); - }, - function (viewsPath, next) { - plugins.fireHook('static:templates.precompile', {}, next); - }, - function (next) { - plugins.getTemplates(next); - }, - function (_pluginTemplates, next) { - pluginTemplates = _pluginTemplates; - winston.verbose('[meta/templates] Compiling templates'); - - async.parallel({ - coreTpls: function (next) { - file.walk(coreTemplatesPath, next); - }, - baseThemes: function (next) { - async.map(baseTemplatesPaths, function (baseTemplatePath, next) { - file.walk(baseTemplatePath, function (err, paths) { - paths = paths.map(function (tpl) { - return { - base: baseTemplatePath, - path: tpl.replace(baseTemplatePath, ''), - }; - }); - - next(err, paths); - }); - }, next); - }, - }, next); - }, - function (data, next) { - var baseThemes = data.baseThemes; - var coreTpls = data.coreTpls; - var paths = {}; - - coreTpls.forEach(function (el, i) { - paths[coreTpls[i].replace(coreTemplatesPath, '')] = coreTpls[i]; - }); - - baseThemes.forEach(function (baseTpls) { - baseTpls.forEach(function (el, i) { - paths[baseTpls[i].path] = path.join(baseTpls[i].base, baseTpls[i].path); - }); - }); - - for (var tpl in pluginTemplates) { - if (pluginTemplates.hasOwnProperty(tpl)) { - paths[tpl] = pluginTemplates[tpl]; - } - } - - next(null, paths); - }, - ], callback); } +Templates.compile = compile; diff --git a/src/plugins.js b/src/plugins.js index f11ed63494..a1193125e8 100644 --- a/src/plugins.js +++ b/src/plugins.js @@ -138,10 +138,13 @@ Plugins.reloadRoutes = function (callback) { }); }; +// DEPRECATED: remove in v1.8.0 Plugins.getTemplates = function (callback) { var templates = {}; var tplName; + winston.warn('[deprecated] Plugins.getTemplates is DEPRECATED to be removed in v1.8.0'); + Plugins.data.getActive(function (err, plugins) { if (err) { return callback(err); From 7d7c51f8e9331d30ea3790f17cc0755f2f5c87bb Mon Sep 17 00:00:00 2001 From: Peter Jaszkowiak Date: Mon, 15 Jan 2018 00:10:24 -0700 Subject: [PATCH 11/32] Fix `acp.min.js` ENOENT error on CI tests Hopefully --- src/meta/js.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/meta/js.js b/src/meta/js.js index ada16d56be..fb3d12f683 100644 --- a/src/meta/js.js +++ b/src/meta/js.js @@ -343,6 +343,11 @@ JS.buildBundle = function (target, fork, callback) { function (next) { getBundleScriptList(target, next); }, + function (files, next) { + mkdirp(path.join(__dirname, '../../build/public'), function (err) { + next(err, files); + }); + }, function (files, next) { var minify = global.env !== 'development'; var filePath = path.join(__dirname, '../../build/public', fileNames[target]); From ec391f4472906f2b548a09969529b9f6831c35d5 Mon Sep 17 00:00:00 2001 From: "Misty (Bot)" Date: Mon, 15 Jan 2018 09:25:04 +0000 Subject: [PATCH 12/32] Latest translations and fallbacks --- public/language/fr/admin/general/dashboard.json | 4 ++-- public/language/tr/global.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/public/language/fr/admin/general/dashboard.json b/public/language/fr/admin/general/dashboard.json index fbc878c950..6bb5c222e3 100644 --- a/public/language/fr/admin/general/dashboard.json +++ b/public/language/fr/admin/general/dashboard.json @@ -31,8 +31,8 @@ "notices": "Informations", "restart-not-required": "Pas de redémarrage nécessaire", "restart-required": "Redémarrage requis", - "search-plugin-installed": "Recherche un plugin installé", - "search-plugin-not-installed": "Rechercher un plugin non installé", + "search-plugin-installed": "Le plugin de recherche est installé", + "search-plugin-not-installed": "Le plugin de recherche n'est pas installé", "search-plugin-tooltip": "Installer un plugin de recherche depuis la page des plugins pour activer la fonctionnalité de recherche", "control-panel": "Contrôle du système", diff --git a/public/language/tr/global.json b/public/language/tr/global.json index b406e423d1..2600f7683e 100644 --- a/public/language/tr/global.json +++ b/public/language/tr/global.json @@ -53,7 +53,7 @@ "topics": "Başlık", "posts": "İleti", "best": "En İyi", - "votes": "Votes", + "votes": "Oy", "upvoters": "Artı", "upvoted": "Artı", "downvoters": "Eksi", From 3d81bcb1fe7fb82c11e2ccd006b1456df4a682aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 15 Jan 2018 10:52:33 -0500 Subject: [PATCH 13/32] closes #6238 --- public/src/client/account/edit/email.js | 2 +- public/src/client/account/edit/password.js | 7 +++++-- public/src/client/account/edit/username.js | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/public/src/client/account/edit/email.js b/public/src/client/account/edit/email.js index e1a068979a..ae182029f0 100644 --- a/public/src/client/account/edit/email.js +++ b/public/src/client/account/edit/email.js @@ -31,7 +31,7 @@ define('forum/account/edit/email', ['forum/account/header'], function (header) { return app.alertError(err.message); } - ajaxify.go('user/' + ajaxify.data.userslug); + ajaxify.go('user/' + ajaxify.data.userslug + '/edit'); }); return false; diff --git a/public/src/client/account/edit/password.js b/public/src/client/account/edit/password.js index 1585a85577..6aa66a13d8 100644 --- a/public/src/client/account/edit/password.js +++ b/public/src/client/account/edit/password.js @@ -82,8 +82,11 @@ define('forum/account/edit/password', ['forum/account/header', 'translator', 'zx onPasswordConfirmChanged(); return app.alertError(err.message); } - - window.location.href = config.relative_path + '/login'; + if (parseInt(app.user.uid, 10) === parseInt(ajaxify.data.uid, 10)) { + window.location.href = config.relative_path + '/login'; + } else { + ajaxify.go('user/' + ajaxify.data.userslug + '/edit'); + } }); } else { if (!passwordsmatch) { diff --git a/public/src/client/account/edit/username.js b/public/src/client/account/edit/username.js index f00e4d16fe..6178466f24 100644 --- a/public/src/client/account/edit/username.js +++ b/public/src/client/account/edit/username.js @@ -39,7 +39,7 @@ define('forum/account/edit/username', ['forum/account/header'], function (header $('[component="header/usericon"]').css('background-color', data['icon:bgColor']).text(data['icon:text']); } - ajaxify.go('user/' + userslug); + ajaxify.go('user/' + userslug + '/edit'); }); return false; From 81d4766c1a1f7d39e608211c09034826a01ced32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 15 Jan 2018 10:56:25 -0500 Subject: [PATCH 14/32] closes #6236 --- public/language/en-GB/admin/appearance/customise.json | 6 +++--- public/src/admin/appearance/customise.js | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/public/language/en-GB/admin/appearance/customise.json b/public/language/en-GB/admin/appearance/customise.json index a1220ec96d..56c11a2805 100644 --- a/public/language/en-GB/admin/appearance/customise.json +++ b/public/language/en-GB/admin/appearance/customise.json @@ -1,7 +1,7 @@ { - "custom-css": "Custom CSS", - "custom-css.description": "Enter your own CSS declarations here, which will be applied after all other styles.", - "custom-css.enable": "Enable Custom CSS", + "custom-css": "Custom CSS/LESS", + "custom-css.description": "Enter your own CSS/LESS declarations here, which will be applied after all other styles.", + "custom-css.enable": "Enable Custom CSS/LESS", "custom-js": "Custom Javascript", "custom-js.description": "Enter your own javascript here. It will be executed after the page is loaded completely.", diff --git a/public/src/admin/appearance/customise.js b/public/src/admin/appearance/customise.js index 86894f0d63..70393a50ae 100644 --- a/public/src/admin/appearance/customise.js +++ b/public/src/admin/appearance/customise.js @@ -14,7 +14,7 @@ define('admin/appearance/customise', ['admin/settings', 'ace/ace'], function (Se var customHTML = ace.edit('customHTML'); customCSS.setTheme('ace/theme/twilight'); - customCSS.getSession().setMode('ace/mode/css'); + customCSS.getSession().setMode('ace/mode/less'); customCSS.on('change', function () { app.flags = app.flags || {}; From 7b04b4876db955b1885b6a48ce6242a91900e784 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 15 Jan 2018 11:31:38 -0500 Subject: [PATCH 15/32] closes #6241 --- src/meta/minifier.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/meta/minifier.js b/src/meta/minifier.js index ed6a68a625..9305b03809 100644 --- a/src/meta/minifier.js +++ b/src/meta/minifier.js @@ -75,7 +75,7 @@ function forkAction(action, callback) { freeChild(proc); if (message.type === 'error') { - return callback(message.err); + return callback(message.message); } if (message.type === 'end') { @@ -103,7 +103,7 @@ if (process.env.minifier_child) { if (typeof actions[action.act] !== 'function') { process.send({ type: 'error', - err: Error('Unknown action'), + message: 'Unknown action', }); return; } @@ -112,7 +112,7 @@ if (process.env.minifier_child) { if (err) { process.send({ type: 'error', - err: err, + message: err.message, }); return; } From 4b5c87e93ffc1279c3f8e168f5e402265a373c6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 15 Jan 2018 12:08:54 -0500 Subject: [PATCH 16/32] send stack instead of message --- src/meta/minifier.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/meta/minifier.js b/src/meta/minifier.js index 9305b03809..e14761a707 100644 --- a/src/meta/minifier.js +++ b/src/meta/minifier.js @@ -112,7 +112,7 @@ if (process.env.minifier_child) { if (err) { process.send({ type: 'error', - message: err.message, + message: err.stack, }); return; } From b27211f8957e18a062818193bd9fefe315e10e78 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 15 Jan 2018 12:31:49 -0500 Subject: [PATCH 17/32] bump persona, re: nodebb/nodebb-theme-persona#371 --- install/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/package.json b/install/package.json index 7a1ad9c601..2286bca6d5 100644 --- a/install/package.json +++ b/install/package.json @@ -69,7 +69,7 @@ "nodebb-plugin-spam-be-gone": "0.5.1", "nodebb-rewards-essentials": "0.0.9", "nodebb-theme-lavender": "5.0.0", - "nodebb-theme-persona": "7.2.13", + "nodebb-theme-persona": "7.2.14", "nodebb-theme-slick": "1.1.2", "nodebb-theme-vanilla": "8.1.7", "nodebb-widget-essentials": "4.0.1", From 13850e35868199d70ccc9c2a954b40377e547131 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 15 Jan 2018 13:22:59 -0500 Subject: [PATCH 18/32] fix categories not showing up in ACP dropdown if they are links --- src/categories.js | 6 +----- src/controllers/search.js | 4 ++++ 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/categories.js b/src/categories.js index 452762ae38..ab1d277e68 100644 --- a/src/categories.js +++ b/src/categories.js @@ -339,10 +339,6 @@ Categories.buildForSelect = function (uid, privilege, callback) { Categories.buildForSelectCategories = function (categories, callback) { function recursive(category, categoriesData, level, depth) { - if (category.link) { - return; - } - var bullet = level ? '• ' : ''; category.value = category.cid; category.level = level; @@ -358,7 +354,7 @@ Categories.buildForSelectCategories = function (categories, callback) { var categoriesData = []; categories = categories.filter(function (category) { - return category && !category.link && !parseInt(category.parentCid, 10); + return category && !parseInt(category.parentCid, 10); }); categories.forEach(function (category) { diff --git a/src/controllers/search.js b/src/controllers/search.js index f02a61dc31..49e762407a 100644 --- a/src/controllers/search.js +++ b/src/controllers/search.js @@ -54,6 +54,10 @@ searchController.search = function (req, res, next) { return next(err); } + results.categories = results.categories.filter(function (category) { + return category && !category.link; + }); + var categoriesData = [ { value: 'all', text: '[[unread:all_categories]]' }, { value: 'watched', text: '[[category:watched-categories]]' }, From f8f79a54803cf13a5c155645fa048ac250430818 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Mon, 15 Jan 2018 15:05:21 -0500 Subject: [PATCH 19/32] Revert "bump persona, re: nodebb/nodebb-theme-persona#371" This reverts commit b27211f8957e18a062818193bd9fefe315e10e78. --- install/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/package.json b/install/package.json index 2286bca6d5..7a1ad9c601 100644 --- a/install/package.json +++ b/install/package.json @@ -69,7 +69,7 @@ "nodebb-plugin-spam-be-gone": "0.5.1", "nodebb-rewards-essentials": "0.0.9", "nodebb-theme-lavender": "5.0.0", - "nodebb-theme-persona": "7.2.14", + "nodebb-theme-persona": "7.2.13", "nodebb-theme-slick": "1.1.2", "nodebb-theme-vanilla": "8.1.7", "nodebb-widget-essentials": "4.0.1", From e092778ba6bc5ab9bb9c14885ed60bb2876229c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 15 Jan 2018 15:05:33 -0500 Subject: [PATCH 20/32] closes #6200 --- install/web.js | 6 ++++-- src/database/redis.js | 36 ++++++++++++++++++++++-------------- src/install.js | 9 +++++---- 3 files changed, 31 insertions(+), 20 deletions(-) diff --git a/install/web.js b/install/web.js index 92dcdb17d3..d63db06ca3 100644 --- a/install/web.js +++ b/install/web.js @@ -10,6 +10,7 @@ var less = require('less'); var async = require('async'); var uglify = require('uglify-js'); var nconf = require('nconf'); +var _ = require('lodash'); var Benchpress = require('benchpressjs'); var app = express(); @@ -103,14 +104,15 @@ function welcome(req, res) { } function install(req, res) { + var setupEnvVars = _.assign({}, process.env); for (var i in req.body) { if (req.body.hasOwnProperty(i) && !process.env.hasOwnProperty(i)) { - process.env[i.replace(':', '__')] = req.body[i]; + setupEnvVars[i.replace(':', '__')] = req.body[i]; } } var child = require('child_process').fork('app', ['--setup'], { - env: process.env, + env: setupEnvVars, }); child.on('close', function (data) { diff --git a/src/database/redis.js b/src/database/redis.js index e45f8f0ef8..70a8ffbd53 100644 --- a/src/database/redis.js +++ b/src/database/redis.js @@ -37,19 +37,22 @@ redisModule.questions = [ ]; redisModule.init = function (callback) { - redisClient = redisModule.connect(); + callback = callback || function () { }; + redisClient = redisModule.connect({}, function (err) { + if (err) { + winston.error('NodeBB could not connect to your Redis database. Redis returned the following error', err); + return callback(err); + } + redisModule.client = redisClient; + + require('./redis/main')(redisClient, redisModule); + require('./redis/hash')(redisClient, redisModule); + require('./redis/sets')(redisClient, redisModule); + require('./redis/sorted')(redisClient, redisModule); + require('./redis/list')(redisClient, redisModule); - redisModule.client = redisClient; - - require('./redis/main')(redisClient, redisModule); - require('./redis/hash')(redisClient, redisModule); - require('./redis/sets')(redisClient, redisModule); - require('./redis/sorted')(redisClient, redisModule); - require('./redis/list')(redisClient, redisModule); - - if (typeof callback === 'function') { callback(); - } + }); }; redisModule.initSessionStore = function (callback) { @@ -66,7 +69,8 @@ redisModule.initSessionStore = function (callback) { } }; -redisModule.connect = function (options) { +redisModule.connect = function (options, callback) { + callback = callback || function () {}; var redis_socket_or_host = nconf.get('redis:host'); var cxn; @@ -88,7 +92,11 @@ redisModule.connect = function (options) { cxn.on('error', function (err) { winston.error(err.stack); - process.exit(1); + callback(err); + }); + + cxn.on('ready', function () { + callback(); }); if (nconf.get('redis:password')) { @@ -99,7 +107,7 @@ redisModule.connect = function (options) { if (dbIdx >= 0) { cxn.select(dbIdx, function (err) { if (err) { - winston.error('NodeBB could not connect to your Redis database. Redis returned the following error', err); + winston.error('NodeBB could not select Redis database. Redis returned the following error', err); throw err; } }); diff --git a/src/install.js b/src/install.js index 2906adc9e8..33bd5a58d1 100644 --- a/src/install.js +++ b/src/install.js @@ -157,16 +157,17 @@ function completeConfigSetup(config, next) { } } + nconf.overrides(config); async.waterfall([ - function (next) { - install.save(config, next); - }, function (next) { require('./database').init(next); }, function (next) { require('./database').createIndices(next); }, + function (next) { + install.save(config, next); + }, ], next); } @@ -523,7 +524,7 @@ install.setup = function (callback) { ], function (err, results) { if (err) { winston.warn('NodeBB Setup Aborted.\n ' + err.stack); - process.exit(); + process.exit(1); } else { var data = {}; if (results[6]) { From f138d3cb70e96a6124c543092397c907d959d9d5 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 15 Jan 2018 15:28:46 -0500 Subject: [PATCH 21/32] adding noopener and noreferrer to links in widgets --- public/src/utils.js | 14 ++++++++++++++ public/src/widgets.js | 1 + 2 files changed, 15 insertions(+) diff --git a/public/src/utils.js b/public/src/utils.js index 0e300d2772..078396fdeb 100644 --- a/public/src/utils.js +++ b/public/src/utils.js @@ -545,6 +545,20 @@ return str.toString().replace(escapeChars, replaceChar); }, + addNoReferrer: function (containerEl) { + containerEl.find('a').attr('rel', function (idx, value) { + value = value ? value.split(' ') : []; + + ['noopener', 'noreferrer'].forEach(function (property) { + if (!value.includes(property)) { + value.push(property); + } + }); + + return value.join(' '); + }); + }, + isAndroidBrowser: function () { // http://stackoverflow.com/questions/9286355/how-to-detect-only-the-native-android-browser var nua = navigator.userAgent; diff --git a/public/src/widgets.js b/public/src/widgets.js index 51f6e0d2be..b8e22ab276 100644 --- a/public/src/widgets.js +++ b/public/src/widgets.js @@ -59,6 +59,7 @@ title: $(this).attr('title'), }); }); + utils.addNoReferrer(widgetAreas); $(window).trigger('action:widgets.loaded', {}); callback(); }; From 66a745d27abc81e5e6a3a3c86b2eb7e05324d144 Mon Sep 17 00:00:00 2001 From: "Misty (Bot)" Date: Tue, 16 Jan 2018 09:25:51 +0000 Subject: [PATCH 22/32] Latest translations and fallbacks --- public/language/sr/error.json | 6 +++--- public/language/sr/global.json | 2 +- public/language/sr/pages.json | 2 +- public/language/sr/topic.json | 2 +- public/language/zh-CN/admin/general/homepage.json | 2 +- public/language/zh-CN/admin/manage/users.json | 10 +++++----- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/public/language/sr/error.json b/public/language/sr/error.json index f99bb32e55..3ec59d1194 100644 --- a/public/language/sr/error.json +++ b/public/language/sr/error.json @@ -17,7 +17,7 @@ "invalid-login-credentials": "Неважећи акредитиви за пријављивање", "invalid-username-or-password": "Молимо наведите и корисничко име и лозинку", "invalid-search-term": "Неисправан упит за претрагу", - "invalid-url": "Invalid URL", + "invalid-url": "Неважећа адреса", "csrf-invalid": "Нисмо успели да вас пријавимо, вероватно због истека сесије. Молимо покушајте поново", "invalid-pagination-value": "Неважећа вредност приликом нумерисања страница, мора бити најмање %1 а највише %2 ", "username-taken": "Корисничко име је заузето", @@ -114,8 +114,8 @@ "cant-edit-chat-message": "Није вам дозвољено да уређујете ову поруку", "cant-remove-last-user": "Не можете уклонити последњег корисника", "cant-delete-chat-message": "Није вам дозвољено да избришете ову поруку", - "chat-edit-duration-expired": "You are only allowed to edit chat messages for %1 second(s) after posting", - "chat-delete-duration-expired": "You are only allowed to delete chat messages for %1 second(s) after posting", + "chat-edit-duration-expired": "Време у којем вам је дозвољено уређивање порука ћаскања након објављивања: %1 сек.", + "chat-delete-duration-expired": "Време у којем вам је дозвољено брисање порука ћаскања након објављивања: %1 сек.", "already-voting-for-this-post": "Већ сте гласали за ову поруку.", "reputation-system-disabled": "Угледи су онемогућени.", "downvoting-disabled": "Негативно гласање је онемогућено", diff --git a/public/language/sr/global.json b/public/language/sr/global.json index 7f1ee9c5f7..1742fe768c 100644 --- a/public/language/sr/global.json +++ b/public/language/sr/global.json @@ -53,7 +53,7 @@ "topics": "Теме", "posts": "Поруке", "best": "Најбоље", - "votes": "Votes", + "votes": "Гласови", "upvoters": "Позитивно гласали", "upvoted": "Позитивно гласано", "downvoters": "Негативно гласали", diff --git a/public/language/sr/pages.json b/public/language/sr/pages.json index 24d60058ce..b7391c0af5 100644 --- a/public/language/sr/pages.json +++ b/public/language/sr/pages.json @@ -6,7 +6,7 @@ "popular-month": "Популарне теме овог месеца", "popular-alltime": "Популарне теме свих времена", "recent": "Недавне теме", - "top": "Top Voted Topics", + "top": "Најгласаније теме", "moderator-tools": "Алати модератора", "flagged-content": "Садржај означен заставицом", "ip-blacklist": "Црна листа IP адреса", diff --git a/public/language/sr/topic.json b/public/language/sr/topic.json index 35a41259f7..44766303f3 100644 --- a/public/language/sr/topic.json +++ b/public/language/sr/topic.json @@ -52,7 +52,7 @@ "not-watching.description": "Немој ме обавештавати о новим одговорима.
Прикажи тему у непрочитаним ако категорија није игнорисана.", "ignoring.description": "Немој ме обавештавати о новим одговорима.
Не приказуј тему у непрочитаним", "thread_tools.title": "Алатке теме", - "thread_tools.markAsUnreadForAll": "Mark Unread For All", + "thread_tools.markAsUnreadForAll": "Означи као непрочитано за све", "thread_tools.pin": "Закачи тему", "thread_tools.unpin": "Откачи тему", "thread_tools.lock": "Закључај тему", diff --git a/public/language/zh-CN/admin/general/homepage.json b/public/language/zh-CN/admin/general/homepage.json index 95cf29899f..8864e4eb34 100644 --- a/public/language/zh-CN/admin/general/homepage.json +++ b/public/language/zh-CN/admin/general/homepage.json @@ -4,5 +4,5 @@ "home-page-route": "主页路由", "custom-route": "自定义路由", "allow-user-home-pages": "允许用户主页", - "home-page-title": "Title of the home page (default \"Home\")" + "home-page-title": "首页标题(默认“Home”)" } \ No newline at end of file diff --git a/public/language/zh-CN/admin/manage/users.json b/public/language/zh-CN/admin/manage/users.json index 45ad017b77..4538b91287 100644 --- a/public/language/zh-CN/admin/manage/users.json +++ b/public/language/zh-CN/admin/manage/users.json @@ -27,8 +27,8 @@ "pills.banned": "被封禁", "pills.search": "搜寻用户", - "search.uid": "By User ID", - "search.uid-placeholder": "Enter a user ID to search", + "search.uid": "通过用户名", + "search.uid-placeholder": "搜索用户ID", "search.username": "通过用户名", "search.username-placeholder": "输入你想找的用户名", "search.email": "通过邮箱", @@ -71,9 +71,9 @@ "alerts.lockout-reset-success": "闭锁已重置!", "alerts.flag-reset-success": "举报已重置!", "alerts.no-remove-yourself-admin": "你无法撤销自己的管理员身份!", - "alerts.make-admin-success": "User is now administrator.", - "alerts.confirm-remove-admin": "Do you really want to remove this administrator?", - "alerts.remove-admin-success": "User is no longer administrator.", + "alerts.make-admin-success": "用户已成为管理员", + "alerts.confirm-remove-admin": "真的想要删除这个管理员?", + "alerts.remove-admin-success": "此用户不再是管理员", "alerts.make-global-mod-success": "User is now global moderator.", "alerts.confirm-remove-global-mod": "Do you really want to remove this global moderator?", "alerts.remove-global-mod-success": "User is no longer global noderator", From 2b95dfde915bb50a5ebb4566344694ff9d7af4db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Tue, 16 Jan 2018 09:11:44 -0500 Subject: [PATCH 23/32] remove extra hook --- public/src/widgets.js | 1 - 1 file changed, 1 deletion(-) diff --git a/public/src/widgets.js b/public/src/widgets.js index b8e22ab276..8123e18103 100644 --- a/public/src/widgets.js +++ b/public/src/widgets.js @@ -15,7 +15,6 @@ locations.forEach(function (location) { var area = $('#content [widget-area="' + location + '"]'); if (area.length) { - $(window).trigger('action:widgets.loaded', {}); return; } From 3df2ab7cb95a641bf7fe73b33ec4a257beb9eed7 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Tue, 16 Jan 2018 12:22:50 -0500 Subject: [PATCH 24/32] exposing lodash as a client-side module --- src/meta/js.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/meta/js.js b/src/meta/js.js index fb3d12f683..60dd9f706f 100644 --- a/src/meta/js.js +++ b/src/meta/js.js @@ -98,6 +98,7 @@ JS.scripts = { 'jqueryui.js': 'public/vendor/jquery/js/jquery-ui.js', 'zxcvbn.js': 'node_modules/zxcvbn/dist/zxcvbn.js', ace: 'node_modules/ace-builds/src-min', + 'lodash.js': 'node_modules/lodash/lodash.js', }, }; From 2fa24063354ceab5e11b49f7464fcb60ef9d6250 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Tue, 16 Jan 2018 12:27:13 -0500 Subject: [PATCH 25/32] bumping persona --- install/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/package.json b/install/package.json index 7a1ad9c601..3d7347d1cb 100644 --- a/install/package.json +++ b/install/package.json @@ -69,7 +69,7 @@ "nodebb-plugin-spam-be-gone": "0.5.1", "nodebb-rewards-essentials": "0.0.9", "nodebb-theme-lavender": "5.0.0", - "nodebb-theme-persona": "7.2.13", + "nodebb-theme-persona": "7.2.15", "nodebb-theme-slick": "1.1.2", "nodebb-theme-vanilla": "8.1.7", "nodebb-widget-essentials": "4.0.1", From 82f20259441c68bb95f69ecab799063bd9e4d67e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Tue, 16 Jan 2018 14:27:44 -0500 Subject: [PATCH 26/32] disable timeout on post install route --- install/web.js | 1 + 1 file changed, 1 insertion(+) diff --git a/install/web.js b/install/web.js index d63db06ca3..8bfd416785 100644 --- a/install/web.js +++ b/install/web.js @@ -104,6 +104,7 @@ function welcome(req, res) { } function install(req, res) { + req.setTimeout(0); var setupEnvVars = _.assign({}, process.env); for (var i in req.body) { if (req.body.hasOwnProperty(i) && !process.env.hasOwnProperty(i)) { From 843fee62e2e0ed4064b180fef864ec9926d430fb Mon Sep 17 00:00:00 2001 From: psychobunny Date: Tue, 16 Jan 2018 16:01:45 -0500 Subject: [PATCH 27/32] utils.debounce --- public/src/utils.js | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/public/src/utils.js b/public/src/utils.js index 078396fdeb..2fcdfa8157 100644 --- a/public/src/utils.js +++ b/public/src/utils.js @@ -746,6 +746,27 @@ rtrim: function (str) { return str.replace(/\s+$/g, ''); }, + + debounce: function (func, wait, immediate) { + // modified from https://davidwalsh.name/javascript-debounce-function + var timeout; + return function () { + var context = this; + var args = arguments; + var later = function () { + timeout = null; + if (!immediate) { + func.apply(context, args); + } + }; + var callNow = immediate && !timeout; + clearTimeout(timeout); + timeout = setTimeout(later, wait); + if (callNow) { + func.apply(context, args); + } + }; + }, }; /* eslint "no-extend-native": "off" */ From c8a6caa842fb09780ca785a439c9fb040d2792b6 Mon Sep 17 00:00:00 2001 From: Peter Jaszkowiak Date: Tue, 16 Jan 2018 11:43:18 -0700 Subject: [PATCH 28/32] Fix #6244, dpendency auto-install fixed Node caches the result of stat on module directories --- src/cli/index.js | 8 ++++++-- src/cli/package-install.js | 2 ++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/cli/index.js b/src/cli/index.js index 76ceb5b071..e4679a4e19 100644 --- a/src/cli/index.js +++ b/src/cli/index.js @@ -8,9 +8,9 @@ var dirname = require('./paths').baseDir; // check to make sure dependencies are installed try { - require('../../package.json'); + fs.accessSync(path.join(dirname, 'package.json'), fs.constants.R_OK); } catch (e) { - if (e.code === 'MODULE_NOT_FOUND') { + if (e.code === 'ENOENT') { console.warn('package.json not found.'); console.log('Populating package.json...'); @@ -18,6 +18,8 @@ try { packageInstall.preserveExtraneousPlugins(); try { + fs.accessSync(path.join(dirname, 'node_modules/colors/package.json'), fs.constants.R_OK); + require('colors'); console.log('OK'.green); } catch (e) { @@ -29,6 +31,8 @@ try { } try { + fs.accessSync(path.join(dirname, 'node_modules/semver/package.json'), fs.constants.R_OK); + var semver = require('semver'); var defaultPackage = require('../../install/package.json'); diff --git a/src/cli/package-install.js b/src/cli/package-install.js index 1892a48cfc..4c7a21a429 100644 --- a/src/cli/package-install.js +++ b/src/cli/package-install.js @@ -33,6 +33,8 @@ function installAll() { var prod = global.env !== 'development'; var command = 'npm install'; try { + fs.accessSync(path.join(dirname, 'node_modules/nconf/package.json'), fs.constants.R_OK); + var packageManager = require('nconf').get('package_manager'); if (packageManager === 'yarn') { command = 'yarn'; From c0cf9e64fd10046cef38d8b651649b01d8a717c3 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Tue, 16 Jan 2018 16:03:51 -0500 Subject: [PATCH 29/32] remove lodash from built modules, use utils.debounce instead --- src/meta/js.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/meta/js.js b/src/meta/js.js index 60dd9f706f..fb3d12f683 100644 --- a/src/meta/js.js +++ b/src/meta/js.js @@ -98,7 +98,6 @@ JS.scripts = { 'jqueryui.js': 'public/vendor/jquery/js/jquery-ui.js', 'zxcvbn.js': 'node_modules/zxcvbn/dist/zxcvbn.js', ace: 'node_modules/ace-builds/src-min', - 'lodash.js': 'node_modules/lodash/lodash.js', }, }; From ea64a6060b6d4b5976789e90331aae985445b143 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Tue, 16 Jan 2018 16:04:22 -0500 Subject: [PATCH 30/32] bump persona --- install/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/package.json b/install/package.json index 3d7347d1cb..e06f51139e 100644 --- a/install/package.json +++ b/install/package.json @@ -69,7 +69,7 @@ "nodebb-plugin-spam-be-gone": "0.5.1", "nodebb-rewards-essentials": "0.0.9", "nodebb-theme-lavender": "5.0.0", - "nodebb-theme-persona": "7.2.15", + "nodebb-theme-persona": "7.2.16", "nodebb-theme-slick": "1.1.2", "nodebb-theme-vanilla": "8.1.7", "nodebb-widget-essentials": "4.0.1", From f3fda152bfc2582d4e5dab46df1ae2cfec96e44b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Tue, 16 Jan 2018 18:20:57 -0500 Subject: [PATCH 31/32] closes #6216 --- public/language/en-GB/admin/manage/tags.json | 1 + public/src/admin/manage/tags.js | 71 +++++++++++++++----- src/posts/edit.js | 2 +- src/socket.io/admin/tags.js | 12 +++- src/topics/tags.js | 66 +++++++++++++++--- src/views/admin/manage/tags.tpl | 8 +++ test/topics.js | 39 +++++++++-- 7 files changed, 165 insertions(+), 34 deletions(-) diff --git a/public/language/en-GB/admin/manage/tags.json b/public/language/en-GB/admin/manage/tags.json index db40e9f098..df597a6166 100644 --- a/public/language/en-GB/admin/manage/tags.json +++ b/public/language/en-GB/admin/manage/tags.json @@ -6,6 +6,7 @@ "description": "Select tags via clicking and/or dragging, use shift to select multiple.", "create": "Create Tag", "modify": "Modify Tags", + "rename": "Rename Tags", "delete": "Delete Selected Tags", "search": "Search for tags...", "settings": "Click here to visit the tag settings page.", diff --git a/public/src/admin/manage/tags.js b/public/src/admin/manage/tags.js index 717d4bba1d..de67b86743 100644 --- a/public/src/admin/manage/tags.js +++ b/public/src/admin/manage/tags.js @@ -15,6 +15,7 @@ define('admin/manage/tags', [ handleCreate(); handleSearch(); handleModify(); + handleRename(); handleDeleteSelected(); }; @@ -103,15 +104,25 @@ define('admin/manage/tags', [ var modal = $('.bootbox'); var bgColor = modal.find('[data-name="bgColor"]').val(); var color = modal.find('[data-name="color"]').val(); - + var data = []; tagsToModify.each(function (idx, tag) { tag = $(tag); + data.push({ + value: tag.attr('data-tag'), + color: modal.find('[data-name="color"]').val(), + bgColor: modal.find('[data-name="bgColor"]').val(), + }); tag.find('[data-name="bgColor"]').val(bgColor); tag.find('[data-name="color"]').val(color); tag.find('.tag-item').css('background-color', bgColor).css('color', color); + }); - save(tag); + socket.emit('admin.tags.update', data, function (err) { + if (err) { + return app.alertError(err.message); + } + app.alertSuccess('[[admin/manage/tags:alerts.update-success]]'); }); }, }, @@ -122,6 +133,46 @@ define('admin/manage/tags', [ }); } + function handleRename() { + $('#rename').on('click', function () { + var tagsToModify = $('.tag-row.ui-selected'); + if (!tagsToModify.length) { + return; + } + + var firstTag = $(tagsToModify[0]); + var title = tagsToModify.length > 1 ? '[[admin/manage/tags:alerts.editing-multiple]]' : '[[admin/manage/tags:alerts.editing-x, ' + firstTag.find('.tag-item').attr('data-tag') + ']]'; + + var modal = bootbox.dialog({ + title: title, + message: $('.rename-modal').html(), + buttons: { + success: { + label: 'Save', + className: 'btn-primary save', + callback: function () { + var data = []; + tagsToModify.each(function (idx, tag) { + tag = $(tag); + data.push({ + value: tag.attr('data-tag'), + newName: modal.find('[data-name="value"]').val(), + }); + }); + + socket.emit('admin.tags.rename', data, function (err) { + if (err) { + return app.alertError(err.message); + } + app.alertSuccess('[[admin/manage/tags:alerts.update-success]]'); + }); + }, + }, + }, + }); + }); + } + function handleDeleteSelected() { $('#deleteSelected').on('click', function () { var tagsToDelete = $('.tag-row.ui-selected'); @@ -158,21 +209,5 @@ define('admin/manage/tags', [ modal.find('[data-name="bgColor"], [data-name="color"]').each(enableColorPicker); } - function save(tag) { - var data = { - tag: tag.attr('data-tag'), - bgColor: tag.find('[data-name="bgColor"]').val(), - color: tag.find('[data-name="color"]').val(), - }; - - socket.emit('admin.tags.update', data, function (err) { - if (err) { - return app.alertError(err.message); - } - - app.alertSuccess('[[admin/manage/tags:alerts.update-success]]'); - }); - } - return Tags; }); diff --git a/src/posts/edit.js b/src/posts/edit.js index 0a9867feeb..991639af40 100644 --- a/src/posts/edit.js +++ b/src/posts/edit.js @@ -140,7 +140,7 @@ module.exports = function (Posts) { db.setObject('topic:' + tid, results.topic, next); }, function (next) { - topics.updateTags(tid, data.tags, next); + topics.updateTopicTags(tid, data.tags, next); }, function (next) { topics.getTopicTagsObjects(tid, next); diff --git a/src/socket.io/admin/tags.js b/src/socket.io/admin/tags.js index 8fe50790eb..f3c403e704 100644 --- a/src/socket.io/admin/tags.js +++ b/src/socket.io/admin/tags.js @@ -13,11 +13,19 @@ Tags.create = function (socket, data, callback) { }; Tags.update = function (socket, data, callback) { - if (!data) { + if (!Array.isArray(data)) { + return callback(new Error('[[error:invalid-data]]')); + } + + topics.updateTags(data, callback); +}; + +Tags.rename = function (socket, data, callback) { + if (!Array.isArray(data)) { return callback(new Error('[[error:invalid-data]]')); } - topics.updateTag(data.tag, data, callback); + topics.renameTags(data, callback); }; Tags.deleteTags = function (socket, data, callback) { diff --git a/src/topics/tags.js b/src/topics/tags.js index 48cf353005..6d725c2269 100644 --- a/src/topics/tags.js +++ b/src/topics/tags.js @@ -9,7 +9,7 @@ var meta = require('../meta'); var _ = require('lodash'); var plugins = require('../plugins'); var utils = require('../utils'); - +var batch = require('../batch'); module.exports = function (Topics) { Topics.createTags = function (tags, tid, timestamp, callback) { @@ -96,13 +96,61 @@ module.exports = function (Topics) { ], callback); }; - Topics.updateTag = function (tag, data, callback) { - if (!tag) { - return setImmediate(callback, new Error('[[error:invalid-tag]]')); - } - db.setObject('tag:' + tag, data, callback); + Topics.updateTags = function (data, callback) { + async.eachSeries(data, function (tagData, next) { + db.setObject('tag:' + tagData.value, { + color: tagData.color, + bgColor: tagData.bgColor, + }, next); + }, callback); + }; + + Topics.renameTags = function (data, callback) { + async.eachSeries(data, function (tagData, next) { + renameTag(tagData.value, tagData.newName, next); + }, callback); }; + function renameTag(tag, newTagName, callback) { + if (!newTagName || tag === newTagName) { + return setImmediate(callback); + } + async.waterfall([ + function (next) { + Topics.createEmptyTag(newTagName, next); + }, + function (next) { + batch.processSortedSet('tag:' + tag + ':topics', function (tids, next) { + async.waterfall([ + function (next) { + db.sortedSetScores('tag:' + tag + ':topics', tids, next); + }, + function (scores, next) { + db.sortedSetAdd('tag:' + newTagName + ':topics', scores, tids, next); + }, + function (next) { + var keys = tids.map(function (tid) { + return 'topic:' + tid + ':tags'; + }); + + async.series([ + async.apply(db.sortedSetRemove, 'tag:' + tag + ':topics', tids), + async.apply(db.setsRemove, keys, tag), + async.apply(db.setsAdd, keys, newTagName), + ], next); + }, + ], next); + }, next); + }, + function (next) { + Topics.deleteTag(tag, next); + }, + function (next) { + updateTagCount(newTagName, next); + }, + ], callback); + } + function updateTagCount(tag, callback) { callback = callback || function () {}; async.waterfall([ @@ -148,7 +196,9 @@ module.exports = function (Topics) { return 'tag:' + tag; }), next); }, - ], callback); + ], function (err) { + callback(err); + }); }; function removeTagsFromTopics(tags, callback) { @@ -266,7 +316,7 @@ module.exports = function (Topics) { ], callback); }; - Topics.updateTags = function (tid, tags, callback) { + Topics.updateTopicTags = function (tid, tags, callback) { callback = callback || function () {}; async.waterfall([ function (next) { diff --git a/src/views/admin/manage/tags.tpl b/src/views/admin/manage/tags.tpl index 8fa01a1200..56c1c5b8c7 100644 --- a/src/views/admin/manage/tags.tpl +++ b/src/views/admin/manage/tags.tpl @@ -41,6 +41,7 @@

[[admin/manage/tags:description]]

+ @@ -74,4 +75,11 @@ + + diff --git a/test/topics.js b/test/topics.js index 903b6b8db4..559eaab973 100644 --- a/test/topics.js +++ b/test/topics.js @@ -1350,22 +1350,22 @@ describe('Topic\'s', function () { }); }); - it('should error if data.tag is invalid', function (done) { + it('should error if data is not an array', function (done) { socketAdmin.tags.update({ uid: adminUid }, { bgColor: '#ff0000', color: '#00ff00', }, function (err) { - assert.equal(err.message, '[[error:invalid-tag]]'); + assert.equal(err.message, '[[error:invalid-data]]'); done(); }); }); it('should update tag', function (done) { - socketAdmin.tags.update({ uid: adminUid }, { - tag: 'emptytag', + socketAdmin.tags.update({ uid: adminUid }, [{ + value: 'emptytag', bgColor: '#ff0000', color: '#00ff00', - }, function (err) { + }], function (err) { assert.ifError(err); db.getObject('tag:emptytag', function (err, data) { assert.ifError(err); @@ -1376,6 +1376,35 @@ describe('Topic\'s', function () { }); }); + it('should rename tags', function (done) { + async.parallel({ + topic1: function (next) { + topics.post({ uid: adminUid, tags: ['plugins'], title: 'topic tagged with plugins', content: 'topic 1 content', cid: topic.categoryId }, next); + }, + topic2: function (next) { + topics.post({ uid: adminUid, tags: ['plugin'], title: 'topic tagged with plugin', content: 'topic 2 content', cid: topic.categoryId }, next); + }, + }, function (err, result) { + assert.ifError(err); + socketAdmin.tags.rename({ uid: adminUid }, [{ + value: 'plugin', + newName: 'plugins', + }], function (err) { + assert.ifError(err); + topics.getTagTids('plugins', 0, -1, function (err, tids) { + assert.ifError(err); + assert.equal(tids.length, 2); + topics.getTopicTags(result.topic2.topicData.tid, function (err, tags) { + assert.ifError(err); + assert.equal(tags.length, 1); + assert.equal(tags[0], 'plugins'); + done(); + }); + }); + }); + }); + }); + it('should return related topics', function (done) { var meta = require('../src/meta'); meta.config.maximumRelatedTopics = 2; From fdd51f43d8ba2c96b99c5dcea419f84e09833d8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Tue, 16 Jan 2018 18:33:14 -0500 Subject: [PATCH 32/32] fix dirname --- src/cli/package-install.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/cli/package-install.js b/src/cli/package-install.js index 4c7a21a429..af245b08fb 100644 --- a/src/cli/package-install.js +++ b/src/cli/package-install.js @@ -33,8 +33,7 @@ function installAll() { var prod = global.env !== 'development'; var command = 'npm install'; try { - fs.accessSync(path.join(dirname, 'node_modules/nconf/package.json'), fs.constants.R_OK); - + fs.accessSync(path.join(modulesPath, 'nconf/package.json'), fs.constants.R_OK); var packageManager = require('nconf').get('package_manager'); if (packageManager === 'yarn') { command = 'yarn';