diff --git a/CHANGELOG.md b/CHANGELOG.md index 00db835f26..c7359fd095 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,87 @@ +#### 1.12.2 (2019-05-15) + +##### Chores + +* incrementing version number - v1.12.2 (22db818e) +* bump themes #7576 (d349754d) +* bump themes (59bdc970) +* bump themes (abcca134) +* bump themes (551b18cd) +* incrementing version number - v1.12.1 (dd973abe) +* **deps:** + * update dependency lint-staged to v8.1.7 (dc6b49ca) + * update commitlint monorepo (9998e86b) + * update dependency jsdom to v15.1.0 (fcd6dc88) + * update dependency mocha to v6.1.4 (3ff8154b) + * update node:8.16.0 docker digest to b5484d1 (6421f10f) + * update dependency husky to v2.3.0 (c20e3313) + * update dependency nyc to v14.1.1 (#7584) (9047210c) + * update dependency eslint to v5.16.0 (692e2ead) + * update dependency nyc to v14 (9210baf5) + * update dependency husky to v2.2.0 (8a018a5f) + * update dependency lint-staged to v8.1.6 (4e39caf8) + * update dependency husky to v2.1.0 (65ff0bbd) + * update dependency husky to v2 (e81a1dbb) + * update dependency eslint-plugin-import to v2.17.2 (#7546) (c1fb17f9) + * update dependency jsdom to v15 (#7556) (d4d8d98f) + * update dependency jsdom to v14.1.0 (#7555) (ca694fd1) + * update node.js to v8.16.0 (#7554) (f10708e7) + * update dependency eslint-plugin-import to v2.17.1 (69dd8e4d) + * update dependency mocha to v6.1.2 (b7169772) + +##### Documentation Changes + +* updated changelog (93b688d0) + +##### New Features + +* let theme know downvoting is disabled, closes https://github.com/NodeBB/NodeBB/pull/7568 (bd94fbc2) +* closes #7583 (cf5aeace) +* #7319 (9385c8e3) +* add node12 to travis (1a7036a6) +* allow file uploading on registration interstitial (ddffc904) +* #7527 (ba5e1eaa) +* #7515 (c38db4f7) + +##### Bug Fixes + +* #7599 image size measurement erroring out on missing path (0d86781c) +* #7590 updated chat and post edit and delete timeout labels in ACP (4f0dc443) +* tests (3a7e99a5) +* #7586, switchTimeagoLanguage shouldn't discriminate against languages w/o shorthands (1703233f) +* #7576 "Disable password changes" can be sidestepped (50260e13) +* if editing password is disabled in ACP, prevent direct access via route/socket (related: #7576) (e114b16d) +* #7582 (c9ca72d0) +* #7461 (96cb29aa) +* increase batch size (3d938e7b) +* #7564 (bf6fc502) +* group cover upload not working for s3 upload (#7571) (8945ebcb) +* test (b9903120) +* #7539 (c940a733) +* #7565 (07e9b67e) +* #7464 (32cf07d7) +* #7147 (fe6d64cc) +* #7424 (f86d74d8) +* #7562 (09681e6c) +* node12 tests (8775e7e6) +* add post queue to /compose POST route (c6cd6c57) +* remove redis object cache (4df925e7) +* #7545 (74038849) +* failing test from 00552d7183f0416a0caa113fe2f1e658659648f7 (9bf3517d) +* fire filter:register.complete for users approved via registration queue (00552d71) +* #7540 (8778f00b) +* more graceful error handling and output for cli/reset (d3ebda73) +* #6438 only apply whitelist when fields request empty (#7528) (808c4909) +* **deps:** + * update dependency nodebb-theme-vanilla to v10.1.31 (#7589) (a9f9d19b) + * update dependency nodebb-theme-persona to v9.1.36 (#7588) (bd86e58d) + * update dependency mongodb to v3.2.4 (#7581) (26d6d0be) + * update dependency postcss to v7.0.16 (10a47a29) + * update dependency nodebb-theme-persona to v9.1.33 (#7563) (7c4201f2) + * update dependency sharp to v0.22.1 (#7561) (288a25f0) + * update dependency nodebb-plugin-composer-default to v6.2.12 (#7538) (a54f8f00) + * update dependency mongodb to v3.2.3 (97a7f02f) + #### 1.12.1 (2019-04-10) ##### Chores diff --git a/install/package.json b/install/package.json index b8916c86b1..a434991d2f 100644 --- a/install/package.json +++ b/install/package.json @@ -2,7 +2,7 @@ "name": "nodebb", "license": "GPL-3.0", "description": "NodeBB Forum", - "version": "1.12.1", + "version": "1.12.2", "homepage": "http://www.nodebb.org", "repository": { "type": "git", @@ -142,7 +142,7 @@ "grunt-contrib-watch": "1.1.0", "husky": "2.3.0", "jsdom": "15.1.0", - "lint-staged": "8.1.6", + "lint-staged": "8.1.7", "mocha": "6.1.4", "mocha-lcov-reporter": "1.3.0", "nyc": "14.1.1", diff --git a/public/language/en-GB/admin/manage/categories.json b/public/language/en-GB/admin/manage/categories.json index 7e49aef36b..1eda6ea080 100644 --- a/public/language/en-GB/admin/manage/categories.json +++ b/public/language/en-GB/admin/manage/categories.json @@ -45,7 +45,12 @@ "privileges.search-group": "Add Group", "privileges.copy-to-children": "Copy to Children", "privileges.copy-from-category": "Copy from Category", + "privileges.copy-privileges-to-all-categories": "Copy to All Categories", + "privileges.copy-group-privileges-to-children": "Copy this group's privileges to the children of this category.", + "privileges.copy-group-privileges-to-all-categories": "Copy this group's privileges to all categories.", + "privileges.copy-group-privileges-from": "Copy this group's privileges from another category.", "privileges.inherit": "If the registered-users group is granted a specific privilege, all other groups receive an implicit privilege, even if they are not explicitly defined/checked. This implicit privilege is shown to you because all users are part of the registered-users user group, and so, privileges for additional groups need not be explicitly granted.", + "privileges.copy-success": "Privileges copied!", "analytics.back": "Back to Categories List", "analytics.title": "Analytics for \"%1\" category", diff --git a/public/language/en-GB/email.json b/public/language/en-GB/email.json index f8a083bebd..23fd34025d 100644 --- a/public/language/en-GB/email.json +++ b/public/language/en-GB/email.json @@ -18,7 +18,7 @@ "invitation.text1": "%1 has invited you to join %2", "invitation.text2": "Your invitation will expire in %1 days.", - "invitation.ctr": "Click here to create your account.", + "invitation.cta": "Click here to create your account.", "reset.text1": "We received a request to reset your password, possibly because you have forgotten it. If this is not the case, please ignore this email.", "reset.text2": "To continue with the password reset, please click on the following link:", @@ -37,14 +37,13 @@ "digest.subject": "Digest for %1", "digest.title": "Your Daily Digest", - "notif.chat.subject": "New chat message received from %1", - "notif.chat.cta": "Click here to continue the conversation", - "notif.chat.unsub.info": "This chat notification was sent to you due to your subscription settings.", - - "notif.post.cta": "Click here to read the full topic", "notif.post.unsub.info": "This post notification was sent to you due to your subscription settings.", - "notif.cta": "Click here to go to forum", + "notif.cta": "To the forum", + "notif.cta-new-reply": "View Post", + "notif.cta-new-chat": "View Chat", + "notif.test.short": "Testing Notifications", + "notif.test.long": "This is a test of the notifications email. Send help!", "test.text1": "This is a test email to verify that the emailer is set up correctly for your NodeBB.", diff --git a/public/language/pl/admin/development/info.json b/public/language/pl/admin/development/info.json index aa9e025623..1cf80f089f 100644 --- a/public/language/pl/admin/development/info.json +++ b/public/language/pl/admin/development/info.json @@ -7,8 +7,8 @@ "online": "dostępny", "git": "git", "memory": "pamięć", - "load": "system load", - "cpu-usage": "cpu usage", + "load": "obciążenie systemu", + "cpu-usage": "użycie procesora", "uptime": "czas działania", "registered": "Zarejestrowane", diff --git a/public/language/pl/admin/manage/groups.json b/public/language/pl/admin/manage/groups.json index 45241f5e4b..056e7a5c8d 100644 --- a/public/language/pl/admin/manage/groups.json +++ b/public/language/pl/admin/manage/groups.json @@ -1,12 +1,12 @@ { "name": "Nazwa grupy", "badge": "Badge", - "properties": "Properties", + "properties": "Właściwości", "description": "Opis grupy", "member-count": "Liczba użytkowników", "system": "System", - "hidden": "Hidden", - "private": "Private", + "hidden": "Ukryty", + "private": "Prywatny", "edit": "Edytuj", "search-placeholder": "Szukaj", "create": "Utwórz grupę", @@ -21,7 +21,7 @@ "edit.user-title": "Tytuł członków ", "edit.icon": "Ikona grupy", "edit.label-color": "Kolor etykiety grupy", - "edit.text-color": "Group Text Color", + "edit.text-color": "Kolor Tekstu Grupy", "edit.show-badge": "Pokaż etykietę", "edit.private-details": "Jeśli włączone, przystępowanie do grup wymaga zatwierdzenia przez właściciela grupy", "edit.private-override": "Ostrzeżenie: Prywatne grupy są wyłączone w ustawieniach, co powoduje przesłonięcia opcji.", diff --git a/public/language/ru/admin/advanced/database.json b/public/language/ru/admin/advanced/database.json index f1ad2d7ed8..3056b72870 100644 --- a/public/language/ru/admin/advanced/database.json +++ b/public/language/ru/admin/advanced/database.json @@ -18,17 +18,17 @@ "mongo.resident-memory": "Долгосрочная память", "mongo.virtual-memory": "Виртуальная память", "mongo.mapped-memory": "Расширенная память", - "mongo.bytes-in": "Bytes In", - "mongo.bytes-out": "Bytes Out", - "mongo.num-requests": "Number of Requests", + "mongo.bytes-in": "Байт входящих", + "mongo.bytes-out": "Байт исходящих", + "mongo.num-requests": "Количество запросов", "mongo.raw-info": "Сырые данные о MongoDB", - "mongo.unauthorized": "NodeBB was unable to query the MongoDB database for relevant statistics. Please ensure that the user in use by NodeBB contains the "clusterMonitor" role for the "admin" database.", + "mongo.unauthorized": "NodeBB не смог создать запрос к базе данных MongoDB соответствующей статистики. Пожалуйста убедитесь, что пользователь, используемый NodeBB имеет "clusterMonitor" привилегию для "admin" базы данных. ", "redis": "Redis", "redis.version": "Версия Redis", - "redis.keys": "Keys", - "redis.expires": "Expires", - "redis.avg-ttl": "Average TTL", + "redis.keys": "Ключей", + "redis.expires": "Истекает", + "redis.avg-ttl": "Средний TTL", "redis.connected-clients": "Подключенные клиенты", "redis.connected-slaves": "Подключенные устройства", "redis.blocked-clients": "Заблокированные клиенты", @@ -37,10 +37,10 @@ "redis.total-connections-recieved": "Общее число подключений получено", "redis.total-commands-processed": "Команд обработано в общем", "redis.iops": "Мгновенные операции. в секунду", - "redis.iinput": "Instantaneous Input Per Second", - "redis.ioutput": "Instantaneous Output Per Second", - "redis.total-input": "Total Input", - "redis.total-output": "Total Ouput", + "redis.iinput": "Текущих входящих в секунду", + "redis.ioutput": "Текущих исходящих в секунду", + "redis.total-input": "Всего входящих", + "redis.total-output": "Всего исходящих", "redis.keyspace-hits": "Количество ключевых просмотров", "redis.keyspace-misses": "Количество не ключевых просмотров", diff --git a/public/language/ru/admin/development/info.json b/public/language/ru/admin/development/info.json index 674fcee21c..b0a0962b8b 100644 --- a/public/language/ru/admin/development/info.json +++ b/public/language/ru/admin/development/info.json @@ -7,8 +7,8 @@ "online": "онлайн", "git": "git", "memory": "память", - "load": "system load", - "cpu-usage": "cpu usage", + "load": "системная загрузка", + "cpu-usage": "Использование процессора", "uptime": "время работы", "registered": "Зарегистрированных", diff --git a/public/language/ru/admin/general/dashboard.json b/public/language/ru/admin/general/dashboard.json index b1ceeed387..b841ef0344 100644 --- a/public/language/ru/admin/general/dashboard.json +++ b/public/language/ru/admin/general/dashboard.json @@ -65,9 +65,9 @@ "high-presence-topics": "Популярные темы", "graphs.page-views": "Просмотров", - "graphs.page-views-registered": "Page Views Registered", - "graphs.page-views-guest": "Page Views Guest", - "graphs.page-views-bot": "Page Views Bot", + "graphs.page-views-registered": "Зарегистрировано просмотров", + "graphs.page-views-guest": "Просмотров гостей", + "graphs.page-views-bot": "Просмотров ботов", "graphs.unique-visitors": "Уникальных пользователей", "graphs.registered-users": "Зарегистрированных пользователей", "graphs.anonymous-users": "Анонимных пользователей", diff --git a/public/language/ru/admin/general/navigation.json b/public/language/ru/admin/general/navigation.json index 4f0a6caa30..2fee361bf4 100644 --- a/public/language/ru/admin/general/navigation.json +++ b/public/language/ru/admin/general/navigation.json @@ -5,11 +5,11 @@ "tooltip": "Подсказка:", "text": "Текст:", "text-class": "Класс текста: опциоально", - "class": "Class: optional", + "class": "Класс: опционально", "id": "ID: опциоанально", "properties": "Свойства:", - "groups": "Groups:", + "groups": "Группы:", "open-new-window": "Открывать в новом окне", "btn.delete": "Удалить", diff --git a/public/language/ru/admin/manage/categories.json b/public/language/ru/admin/manage/categories.json index b24f8d97d4..36698f8ad3 100644 --- a/public/language/ru/admin/manage/categories.json +++ b/public/language/ru/admin/manage/categories.json @@ -17,7 +17,7 @@ "parent-category": "Родительская категория", "optional-parent-category": "(не обязательно) Родительская категория\n", "parent-category-none": "(Пусто)", - "copy-parent": "Copy Parent", + "copy-parent": "Скопировать Родительский", "copy-settings": "Копировать настройки из", "optional-clone-settings": "(не обязательно) Копировать настройки из", "clone-children": "Дублировать вложенные категориии настройки", @@ -30,13 +30,13 @@ "select-category": "Указать категорию", "set-parent-category": "Указать родительскую категорию", - "privileges.description": "You can configure the access control privileges for portions of the site in this section. Privileges can be granted on a per-user or a per-group basis. Select the domain of effect from the dropdown below.", - "privileges.category-selector": "Configuring privileges for ", + "privileges.description": "В этой секции вы можете настроить права на управление доступом. Права могут быть предоставлены как на пользователя, так и на группу. Выберите применяемый домен с помощью формы поиска ниже.", + "privileges.category-selector": "Настройка привилегий для", "privileges.warning": "Замечание: Настройки прав применяются немедленно. Нет необходимости сохранять категорию после изменения настроек.", "privileges.section-viewing": "Права на просмотр", "privileges.section-posting": "Права на создание поста", "privileges.section-moderation": "Права модераторов", - "privileges.section-other": "Other", + "privileges.section-other": "Другое", "privileges.section-user": "Пользователь", "privileges.search-user": "Добавить пользователя", "privileges.no-users": "В этой категории нет специально заданных прав пользователя.", diff --git a/public/language/ru/admin/manage/uploads.json b/public/language/ru/admin/manage/uploads.json index 21bc8201fc..9d3b76a0c8 100644 --- a/public/language/ru/admin/manage/uploads.json +++ b/public/language/ru/admin/manage/uploads.json @@ -1,9 +1,9 @@ { - "upload-file": "Upload File", - "filename": "Filename", + "upload-file": "Загрузить Файл", + "filename": "Имя файла", "usage": "Post Usage", - "orphaned": "Orphaned", - "size/filecount": "Size / Filecount", - "confirm-delete": "Do you really want to delete this file?", - "filecount": "%1 files" + "orphaned": "Отделенный", + "size/filecount": "Размер / Файлов", + "confirm-delete": "Вы действительно хотите удалить этот файл?", + "filecount": "%1 файлов" } \ No newline at end of file diff --git a/public/language/ru/notifications.json b/public/language/ru/notifications.json index f3b479299a..b9ae7b8e87 100644 --- a/public/language/ru/notifications.json +++ b/public/language/ru/notifications.json @@ -8,7 +8,7 @@ "outgoing_link_message": "Вы сейчас читаете: %1", "continue_to": "Перейти на %1", "return_to": "Вернуться к %1", - "new_notification": "You have a new notification", + "new_notification": "У вас новое уведомление", "you_have_unread_notifications": "У вас есть непрочитанные уведомления.", "all": "Все", "topics": "Темы", diff --git a/public/src/admin/manage/privileges.js b/public/src/admin/manage/privileges.js index f43ebef04f..86b2351d36 100644 --- a/public/src/admin/manage/privileges.js +++ b/public/src/admin/manage/privileges.js @@ -49,8 +49,29 @@ define('admin/manage/privileges', [ $('.privilege-table-container').on('click', '[data-action="search.user"]', Privileges.addUserToPrivilegeTable); $('.privilege-table-container').on('click', '[data-action="search.group"]', Privileges.addGroupToPrivilegeTable); - $('.privilege-table-container').on('click', '[data-action="copyToChildren"]', Privileges.copyPrivilegesToChildren); - $('.privilege-table-container').on('click', '[data-action="copyPrivilegesFrom"]', Privileges.copyPrivilegesFromCategory); + $('.privilege-table-container').on('click', '[data-action="copyToChildren"]', function () { + Privileges.copyPrivilegesToChildren(cid, ''); + }); + $('.privilege-table-container').on('click', '[data-action="copyToChildrenGroup"]', function () { + var groupName = $(this).parents('[data-group-name]').attr('data-group-name'); + Privileges.copyPrivilegesToChildren(cid, groupName); + }); + + $('.privilege-table-container').on('click', '[data-action="copyPrivilegesFrom"]', function () { + Privileges.copyPrivilegesFromCategory(cid, ''); + }); + $('.privilege-table-container').on('click', '[data-action="copyPrivilegesFromGroup"]', function () { + var groupName = $(this).parents('[data-group-name]').attr('data-group-name'); + Privileges.copyPrivilegesFromCategory(cid, groupName); + }); + + $('.privilege-table-container').on('click', '[data-action="copyToAll"]', function () { + Privileges.copyPrivilegesToAllCategories(cid, ''); + }); + $('.privilege-table-container').on('click', '[data-action="copyToAllGroup"]', function () { + var groupName = $(this).parents('[data-group-name]').attr('data-group-name'); + Privileges.copyPrivilegesToAllCategories(cid, groupName); + }); Privileges.exposeAssumedPrivileges(); }; @@ -168,18 +189,18 @@ define('admin/manage/privileges', [ }); }; - Privileges.copyPrivilegesToChildren = function () { - socket.emit('admin.categories.copyPrivilegesToChildren', cid, function (err) { + Privileges.copyPrivilegesToChildren = function (cid, group) { + socket.emit('admin.categories.copyPrivilegesToChildren', { cid: cid, group: group }, function (err) { if (err) { return app.alertError(err.message); } - app.alertSuccess('Privileges copied!'); + app.alertSuccess('[[admin/manage/categories:privileges.copy-success]]'); }); }; - Privileges.copyPrivilegesFromCategory = function () { + Privileges.copyPrivilegesFromCategory = function (cid, group) { categorySelector.modal(ajaxify.data.categories.slice(1), function (fromCid) { - socket.emit('admin.categories.copyPrivilegesFrom', { toCid: cid, fromCid: fromCid }, function (err) { + socket.emit('admin.categories.copyPrivilegesFrom', { toCid: cid, fromCid: fromCid, group: group }, function (err) { if (err) { return app.alertError(err.message); } @@ -188,5 +209,14 @@ define('admin/manage/privileges', [ }); }; + Privileges.copyPrivilegesToAllCategories = function (cid, group) { + socket.emit('admin.categories.copyPrivilegesToAllCategories', { cid: cid, group: group }, function (err) { + if (err) { + return app.alertError(err.message); + } + app.alertSuccess('[[admin/manage/categories:privileges.copy-success]]'); + }); + }; + return Privileges; }); diff --git a/public/src/ajaxify.js b/public/src/ajaxify.js index d8753b9a9f..7e0931511e 100644 --- a/public/src/ajaxify.js +++ b/public/src/ajaxify.js @@ -196,7 +196,9 @@ $(document).ready(function () { $('#content, #footer').removeClass('ajaxifying'); + // Only executed on ajaxify. Otherwise these'd be in ajaxify.end() app.refreshTitle(data.title); + app.updateTags(); }); }); } diff --git a/public/src/app.js b/public/src/app.js index 9ca5678f4f..1772f63e23 100644 --- a/public/src/app.js +++ b/public/src/app.js @@ -864,4 +864,68 @@ app.cacheBuster = null; document.head.appendChild(linkEl); }; + + app.updateTags = function () { + var metaWhitelist = ['title', 'description', /og:.+/, /article:.+/].map(function (val) { + return new RegExp(val); + }); + var linkWhitelist = ['canonical', 'alternate', 'up']; + + // Delete the old meta tags + Array.prototype.slice + .call(document.querySelectorAll('head meta')) + .filter(function (el) { + var name = el.getAttribute('property') || el.getAttribute('name'); + return metaWhitelist.some(function (exp) { + return !!exp.test(name); + }); + }) + .forEach(function (el) { + document.head.removeChild(el); + }); + + // Add new meta tags + ajaxify.data._header.tags.meta + .filter(function (tagObj) { + var name = tagObj.name || tagObj.property; + return metaWhitelist.some(function (exp) { + return !!exp.test(name); + }); + }) + .forEach(function (tagObj) { + var metaEl = document.createElement('meta'); + Object.keys(tagObj).forEach(function (prop) { + metaEl.setAttribute(prop, tagObj[prop]); + }); + document.head.appendChild(metaEl); + }); + + // Delete the old link tags + Array.prototype.slice + .call(document.querySelectorAll('head link')) + .filter(function (el) { + var name = el.getAttribute('rel'); + return linkWhitelist.some(function (item) { + return item === name; + }); + }) + .forEach(function (el) { + document.head.removeChild(el); + }); + + // Add new link tags + ajaxify.data._header.tags.link + .filter(function (tagObj) { + return linkWhitelist.some(function (item) { + return item === tagObj.rel; + }); + }) + .forEach(function (tagObj) { + var linkEl = document.createElement('link'); + Object.keys(tagObj).forEach(function (prop) { + linkEl.setAttribute(prop, tagObj[prop]); + }); + document.head.appendChild(linkEl); + }); + }; }()); diff --git a/src/categories/create.js b/src/categories/create.js index 4836db2d0b..3c1c23a354 100644 --- a/src/categories/create.js +++ b/src/categories/create.js @@ -208,18 +208,28 @@ module.exports = function (Categories) { ], callback); } - Categories.copyPrivilegesFrom = function (fromCid, toCid, callback) { + Categories.copyPrivilegesFrom = function (fromCid, toCid, group, callback) { + if (typeof group === 'function') { + callback = group; + group = ''; + } + async.waterfall([ function (next) { plugins.fireHook('filter:categories.copyPrivilegesFrom', { privileges: privileges.privilegeList.slice(), fromCid: fromCid, toCid: toCid, + group: group, }, next); }, function (data, next) { async.each(data.privileges, function (privilege, next) { - copyPrivilege(privilege, data.fromCid, data.toCid, next); + if (group) { + copyPrivilegeByGroup(privilege, data.fromCid, data.toCid, group, next); + } else { + copyPrivilege(privilege, data.fromCid, data.toCid, next); + } }, next); }, ], callback); @@ -249,4 +259,22 @@ module.exports = function (Categories) { }, ], callback); } + + function copyPrivilegeByGroup(privilege, fromCid, toCid, group, callback) { + async.waterfall([ + function (next) { + groups.leave('cid:' + toCid + ':privileges:' + privilege, group, next); + }, + function (next) { + db.isSortedSetMember('group:cid:' + fromCid + ':privileges:' + privilege + ':members', group, next); + }, + function (isMember, next) { + if (!isMember) { + return callback(); + } + + groups.join('cid:' + toCid + ':privileges:' + privilege, group, next); + }, + ], callback); + } }; diff --git a/src/middleware/header.js b/src/middleware/header.js index 5bdab71f55..b347723ddb 100644 --- a/src/middleware/header.js +++ b/src/middleware/header.js @@ -107,7 +107,6 @@ module.exports = function (middleware) { }); }, navigation: async.apply(navigation.get, req.uid), - tags: async.apply(meta.tags.parse, req, data, res.locals.metaTags, res.locals.linkTags), banned: async.apply(user.bans.isBanned, req.uid), banReason: async.apply(user.bans.getReason, req.uid), @@ -180,8 +179,8 @@ module.exports = function (middleware) { templateValues.browserTitle = results.browserTitle; templateValues.navigation = results.navigation; templateValues.unreadCount = unreadCount; - templateValues.metaTags = results.tags.meta; - templateValues.linkTags = results.tags.link; + templateValues.metaTags = data._header.tags.meta; + templateValues.linkTags = data._header.tags.link; templateValues.isAdmin = results.user.isAdmin; templateValues.isGlobalMod = results.user.isGlobalMod; templateValues.showModMenu = results.user.isAdmin || results.user.isGlobalMod || results.user.isMod; diff --git a/src/middleware/render.js b/src/middleware/render.js index 56ee550504..c33d6a8eac 100644 --- a/src/middleware/render.js +++ b/src/middleware/render.js @@ -6,6 +6,7 @@ var validator = require('validator'); var winston = require('winston'); var plugins = require('../plugins'); +var meta = require('../meta'); var translator = require('../translator'); var widgets = require('../widgets'); var utils = require('../utils'); @@ -49,6 +50,14 @@ module.exports = function (middleware) { templateToRender = data.templateData.templateToRender || template; plugins.fireHook('filter:middleware.render', { req: req, res: res, templateData: data.templateData }, next); }, + function parseTags(data, next) { + meta.tags.parse(req, data, res.locals.metaTags, res.locals.linkTags, function (err, tags) { + options._header = { + tags: tags, + }; + next(err, data); + }); + }, function (data, next) { options = data.templateData; diff --git a/src/notifications.js b/src/notifications.js index b2078d2928..27b1fb0439 100644 --- a/src/notifications.js +++ b/src/notifications.js @@ -241,6 +241,11 @@ function pushToUids(uids, notification, callback) { } function sendEmail(uids, callback) { + // Update CTA messaging (as not all notification types need custom text) + if (['new-reply', 'new-chat'].includes(notification.type)) { + notification['cta-type'] = notification.type; + } + async.eachLimit(uids, 3, function (uid, next) { emailer.send('notification', uid, { path: notification.path, diff --git a/src/socket.io/admin.js b/src/socket.io/admin.js index a8c0537e20..eee514384d 100644 --- a/src/socket.io/admin.js +++ b/src/socket.io/admin.js @@ -279,8 +279,8 @@ SocketAdmin.email.test = function (socket, data, callback) { function (next) { notifications.create({ type: 'test', - bodyShort: '[[admin-settings-email:testing]]', - bodyLong: '[[admin-settings-email:testing.send-help]]', + bodyShort: '[[email:notif.test.short]]', + bodyLong: '[[email:notif.test.long]]', nid: 'uid:' + socket.uid + ':test', path: '/', from: socket.uid, diff --git a/src/socket.io/admin/categories.js b/src/socket.io/admin/categories.js index d585318f17..9497abed61 100644 --- a/src/socket.io/admin/categories.js +++ b/src/socket.io/admin/categories.js @@ -101,25 +101,25 @@ Categories.getPrivilegeSettings = function (socket, cid, callback) { } }; -Categories.copyPrivilegesToChildren = function (socket, cid, callback) { +Categories.copyPrivilegesToChildren = function (socket, data, callback) { async.waterfall([ function (next) { - categories.getChildren([cid], socket.uid, next); + categories.getChildren([data.cid], socket.uid, next); }, function (children, next) { children = children[0]; async.eachSeries(children, function (child, next) { - copyPrivilegesToChildrenRecursive(cid, child, next); + copyPrivilegesToChildrenRecursive(data.cid, child, data.group, next); }, next); }, ], callback); }; -function copyPrivilegesToChildrenRecursive(parentCid, category, callback) { +function copyPrivilegesToChildrenRecursive(parentCid, category, group, callback) { async.waterfall([ function (next) { - categories.copyPrivilegesFrom(parentCid, category.cid, next); + categories.copyPrivilegesFrom(parentCid, category.cid, group, next); }, function (next) { async.eachSeries(category.children, function (child, next) { @@ -134,5 +134,19 @@ Categories.copySettingsFrom = function (socket, data, callback) { }; Categories.copyPrivilegesFrom = function (socket, data, callback) { - categories.copyPrivilegesFrom(data.fromCid, data.toCid, callback); + categories.copyPrivilegesFrom(data.fromCid, data.toCid, data.group, callback); +}; + +Categories.copyPrivilegesToAllCategories = function (socket, data, callback) { + async.waterfall([ + function (next) { + categories.getAllCidsFromSet('categories:cid', next); + }, + function (cids, next) { + cids = cids.filter(cid => parseInt(cid, 10) !== parseInt(data.cid, 10)); + async.eachSeries(cids, function (toCid, next) { + categories.copyPrivilegesFrom(data.cid, toCid, data.group, next); + }, next); + }, + ], callback); }; diff --git a/src/views/admin/partials/categories/privileges.tpl b/src/views/admin/partials/categories/privileges.tpl index 9e0b8632d2..be5d892545 100644 --- a/src/views/admin/partials/categories/privileges.tpl +++ b/src/views/admin/partials/categories/privileges.tpl @@ -94,7 +94,18 @@ {privileges.groups.name} - + + + {function.spawnPrivilegeStates, privileges.groups.name, ../privileges} @@ -104,11 +115,14 @@ + - diff --git a/src/views/emails/digest.tpl b/src/views/emails/digest.tpl index 34b319c7b2..549cdd8b79 100644 --- a/src/views/emails/digest.tpl +++ b/src/views/emails/digest.tpl @@ -85,7 +85,7 @@ -     [[email:digest.cta, {site_title}]]     + [[email:digest.cta, {site_title}]] → diff --git a/src/views/emails/invitation.tpl b/src/views/emails/invitation.tpl index 64d60307d1..7fad0c1362 100644 --- a/src/views/emails/invitation.tpl +++ b/src/views/emails/invitation.tpl @@ -28,7 +28,7 @@ -     [[email:invitation.ctr]]     + [[email:invitation.cta]] → diff --git a/src/views/emails/notification.tpl b/src/views/emails/notification.tpl index 489446e468..b9fadef623 100644 --- a/src/views/emails/notification.tpl +++ b/src/views/emails/notification.tpl @@ -31,7 +31,7 @@ -     [[email:notif.cta]]     + [[email:notif.cta-{notification.cta-type}]] → diff --git a/src/views/emails/registration_accepted.tpl b/src/views/emails/registration_accepted.tpl index a1368e8727..35c1d50b53 100644 --- a/src/views/emails/registration_accepted.tpl +++ b/src/views/emails/registration_accepted.tpl @@ -24,7 +24,7 @@ -     [[email:digest.cta, {site_title}]]     + [[email:digest.cta, {site_title}]] → diff --git a/src/views/emails/reset.tpl b/src/views/emails/reset.tpl index 0b78f51c16..ffa92f7f0b 100644 --- a/src/views/emails/reset.tpl +++ b/src/views/emails/reset.tpl @@ -31,7 +31,7 @@ -     [[email:reset.cta]]     + [[email:reset.cta]] → diff --git a/src/views/emails/reset_notify.tpl b/src/views/emails/reset_notify.tpl index 03b2c27ac2..65b2c32f13 100644 --- a/src/views/emails/reset_notify.tpl +++ b/src/views/emails/reset_notify.tpl @@ -31,7 +31,7 @@ -     [[email:digest.cta, {site_title}]]     + [[email:digest.cta, {site_title}]] → diff --git a/src/views/emails/test.tpl b/src/views/emails/test.tpl index 5192c6776c..267978dc13 100644 --- a/src/views/emails/test.tpl +++ b/src/views/emails/test.tpl @@ -24,7 +24,7 @@ -     [[email:digest.cta, {site_title}]]     + [[email:digest.cta, {site_title}]] → diff --git a/src/views/emails/verify_email.tpl b/src/views/emails/verify_email.tpl index d4107cdd5b..c5ec237065 100644 --- a/src/views/emails/verify_email.tpl +++ b/src/views/emails/verify_email.tpl @@ -31,7 +31,7 @@ -     [[email:welcome.cta]]     + [[email:welcome.cta]] → diff --git a/src/views/emails/welcome.tpl b/src/views/emails/welcome.tpl index 01dd5f04b6..dfc5e9ed22 100644 --- a/src/views/emails/welcome.tpl +++ b/src/views/emails/welcome.tpl @@ -20,7 +20,7 @@ -     [[email:welcome.cta]]     + [[email:welcome.cta]] → diff --git a/test/categories.js b/test/categories.js index 69e5fddbee..30e5c30842 100644 --- a/test/categories.js +++ b/test/categories.js @@ -505,7 +505,7 @@ describe('Categories', function () { socketCategories.setPrivilege({ uid: adminUid }, { cid: parentCid, privilege: 'groups:topics:delete', set: true, member: 'registered-users' }, next); }, function (next) { - socketCategories.copyPrivilegesToChildren({ uid: adminUid }, parentCid, next); + socketCategories.copyPrivilegesToChildren({ uid: adminUid }, { cid: parentCid, group: '' }, next); }, function (next) { privileges.categories.can('topics:delete', child2Cid, posterUid, next); @@ -561,7 +561,7 @@ describe('Categories', function () { ], done); }); - it('should copy privileges from', function (done) { + it('should copy privileges from another category', function (done) { var child1Cid; var parentCid; async.waterfall([ @@ -588,6 +588,34 @@ describe('Categories', function () { }, ], done); }); + + it('should copy privileges from another category for a single group', function (done) { + var child1Cid; + var parentCid; + async.waterfall([ + function (next) { + Categories.create({ name: 'parent', description: 'copy me' }, next); + }, + function (category, next) { + parentCid = category.cid; + Categories.create({ name: 'child1' }, next); + }, + function (category, next) { + child1Cid = category.cid; + socketCategories.setPrivilege({ uid: adminUid }, { cid: parentCid, privilege: 'groups:topics:delete', set: true, member: 'registered-users' }, next); + }, + function (next) { + socketCategories.copyPrivilegesFrom({ uid: adminUid }, { fromCid: parentCid, toCid: child1Cid, group: 'registered-users' }, next); + }, + function (next) { + privileges.categories.can('topics:delete', child1Cid, 0, next); + }, + function (canDelete, next) { + assert(!canDelete); + next(); + }, + ], done); + }); }); it('should get active users', function (done) {