v1.18.x
Barış Soner Uşaklı 6 years ago
commit f326f53d4f

@ -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

@ -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",

@ -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 <code>registered-users</code> group is granted a specific privilege, all other groups receive an <strong>implicit privilege</strong>, even if they are not explicitly defined/checked. This implicit privilege is shown to you because all users are part of the <code>registered-users</code> 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",

@ -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.",

@ -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",

@ -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.",

@ -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 &quot;clusterMonitor&quot; role for the &quot;admin&quot; database.",
"mongo.unauthorized": "NodeBB не смог создать запрос к базе данных MongoDB соответствующей статистики. Пожалуйста убедитесь, что пользователь, используемый NodeBB имеет &quot;clusterMonitor&quot; привилегию для &quot;admin&quot; базы данных. ",
"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": "Количество не ключевых просмотров",

@ -7,8 +7,8 @@
"online": "онлайн",
"git": "git",
"memory": "память",
"load": "system load",
"cpu-usage": "cpu usage",
"load": "системная загрузка",
"cpu-usage": "Использование процессора",
"uptime": "время работы",
"registered": "Зарегистрированных",

@ -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": "Анонимных пользователей",

@ -5,11 +5,11 @@
"tooltip": "Подсказка:",
"text": "Текст:",
"text-class": "Класс текста: <small>опциоально</small>",
"class": "Class: <small>optional</small>",
"class": "Класс: <small>опционально</small>",
"id": "ID: <small>опциоанально</small>",
"properties": "Свойства:",
"groups": "Groups:",
"groups": "Группы:",
"open-new-window": "Открывать в новом окне",
"btn.delete": "Удалить",

@ -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": "<strong>Замечание</strong>: Настройки прав применяются немедленно. Нет необходимости сохранять категорию после изменения настроек.",
"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": "В этой категории нет специально заданных прав пользователя.",

@ -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 файлов"
}

@ -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": "Темы",

@ -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;
});

@ -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();
});
});
}

@ -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);
});
};
}());

@ -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);
}
};

@ -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;

@ -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;

@ -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,

@ -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,

@ -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);
};

@ -94,7 +94,18 @@
<!-- ENDIF privileges.groups.isPrivate -->
{privileges.groups.name}
</td>
<td></td>
<td>
<div class="dropdown">
<button class="btn btn-default btn-sm dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
<i class="fa fa-copy"></i>
</button>
<ul class="dropdown-menu" aria-labelledby="dropdownMenu1">
<li data-action="copyToAllGroup"><a href="#">[[admin/manage/categories:privileges.copy-group-privileges-to-all-categories]]</a></li>
<li data-action="copyToChildrenGroup"><a href="#">[[admin/manage/categories:privileges.copy-group-privileges-to-children]]</a></li>
<li data-action="copyPrivilegesFromGroup"><a href="#">[[admin/manage/categories:privileges.copy-group-privileges-from]]</a></li>
</ul>
</div>
</td>
{function.spawnPrivilegeStates, privileges.groups.name, ../privileges}
</tr>
<!-- END privileges.groups -->
@ -104,11 +115,14 @@
<button type="button" class="btn btn-primary pull-right" data-ajaxify="false" data-action="search.group">
[[admin/manage/categories:privileges.search-group]]
</button>
<button type="button" class="btn btn-info pull-right" data-ajaxify="false" data-action="copyPrivilegesFrom">
[[admin/manage/categories:privileges.copy-from-category]]
</button>
<button type="button" class="btn btn-info pull-right" data-ajaxify="false" data-action="copyToChildren">
[[admin/manage/categories:privileges.copy-to-children]]
</button>
<button type="button" class="btn btn-info pull-right" data-ajaxify="false" data-action="copyPrivilegesFrom">
[[admin/manage/categories:privileges.copy-from-category]]
<button type="button" class="btn btn-info pull-right" data-ajaxify="false" data-action="copyToAll">
[[admin/manage/categories:privileges.copy-privileges-to-all-categories]]
</button>
</div>
</td>

@ -85,7 +85,7 @@
<tr>
<td style="border-radius: 3px; background: #222222; text-align: center;" class="button-td">
<a href="{url}" style="background: #222222; border: 15px solid #222222; font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol; font-size: 13px; line-height: 1.1; text-align: center; text-decoration: none; display: block; border-radius: 3px; font-weight: bold;" class="button-a">
<span style="color:#ffffff;" class="button-link">&nbsp;&nbsp;&nbsp;&nbsp;[[email:digest.cta, {site_title}]]&nbsp;&nbsp;&nbsp;&nbsp;</span>
<span style="color:#ffffff;" class="button-link">[[email:digest.cta, {site_title}]] &rarr;</span>
</a>
</td>
</tr>

@ -28,7 +28,7 @@
<tr>
<td style="border-radius: 3px; background: #222222; text-align: center;" class="button-td">
<a href="{registerLink}" style="background: #222222; border: 15px solid #222222; font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol; font-size: 13px; line-height: 1.1; text-align: center; text-decoration: none; display: block; border-radius: 3px; font-weight: bold;" class="button-a">
<span style="color:#ffffff;" class="button-link">&nbsp;&nbsp;&nbsp;&nbsp;[[email:invitation.ctr]]&nbsp;&nbsp;&nbsp;&nbsp;</span>
<span style="color:#ffffff;" class="button-link">[[email:invitation.cta]] &rarr;</span>
</a>
</td>
</tr>

@ -31,7 +31,7 @@
<tr>
<td style="border-radius: 3px; background: #222222; text-align: center;" class="button-td">
<a href="{url}{path}" style="background: #222222; border: 15px solid #222222; font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol; font-size: 13px; line-height: 1.1; text-align: center; text-decoration: none; display: block; border-radius: 3px; font-weight: bold;" class="button-a">
<span style="color:#ffffff;" class="button-link">&nbsp;&nbsp;&nbsp;&nbsp;[[email:notif.cta]]&nbsp;&nbsp;&nbsp;&nbsp;</span>
<span style="color:#ffffff;" class="button-link">[[email:notif.cta<!-- IF notification.cta-type -->-{notification.cta-type}<!-- END -->]] &rarr;</span>
</a>
</td>
</tr>

@ -24,7 +24,7 @@
<tr>
<td style="border-radius: 3px; background: #222222; text-align: center;" class="button-td">
<a href="{url}" style="background: #222222; border: 15px solid #222222; font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol; font-size: 13px; line-height: 1.1; text-align: center; text-decoration: none; display: block; border-radius: 3px; font-weight: bold;" class="button-a">
<span style="color:#ffffff;" class="button-link">&nbsp;&nbsp;&nbsp;&nbsp;[[email:digest.cta, {site_title}]]&nbsp;&nbsp;&nbsp;&nbsp;</span>
<span style="color:#ffffff;" class="button-link">[[email:digest.cta, {site_title}]] &rarr;</span>
</a>
</td>
</tr>

@ -31,7 +31,7 @@
<tr>
<td style="border-radius: 3px; background: #222222; text-align: center;" class="button-td">
<a href="{reset_link}" style="background: #222222; border: 15px solid #222222; font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol; font-size: 13px; line-height: 1.1; text-align: center; text-decoration: none; display: block; border-radius: 3px; font-weight: bold;" class="button-a">
<span style="color:#ffffff;" class="button-link">&nbsp;&nbsp;&nbsp;&nbsp;[[email:reset.cta]]&nbsp;&nbsp;&nbsp;&nbsp;</span>
<span style="color:#ffffff;" class="button-link">[[email:reset.cta]] &rarr;</span>
</a>
</td>
</tr>

@ -31,7 +31,7 @@
<tr>
<td style="border-radius: 3px; background: #222222; text-align: center;" class="button-td">
<a href="{url}" style="background: #222222; border: 15px solid #222222; font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol; font-size: 13px; line-height: 1.1; text-align: center; text-decoration: none; display: block; border-radius: 3px; font-weight: bold;" class="button-a">
<span style="color:#ffffff;" class="button-link">&nbsp;&nbsp;&nbsp;&nbsp;[[email:digest.cta, {site_title}]]&nbsp;&nbsp;&nbsp;&nbsp;</span>
<span style="color:#ffffff;" class="button-link">[[email:digest.cta, {site_title}]] &rarr;</span>
</a>
</td>
</tr>

@ -24,7 +24,7 @@
<tr>
<td style="border-radius: 3px; background: #222222; text-align: center;" class="button-td">
<a href="{url}" style="background: #222222; border: 15px solid #222222; font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol; font-size: 13px; line-height: 1.1; text-align: center; text-decoration: none; display: block; border-radius: 3px; font-weight: bold;" class="button-a">
<span style="color:#ffffff;" class="button-link">&nbsp;&nbsp;&nbsp;&nbsp;[[email:digest.cta, {site_title}]]&nbsp;&nbsp;&nbsp;&nbsp;</span>
<span style="color:#ffffff;" class="button-link">[[email:digest.cta, {site_title}]] &rarr;</span>
</a>
</td>
</tr>

@ -31,7 +31,7 @@
<tr>
<td style="border-radius: 3px; background: #222222; text-align: center;" class="button-td">
<a href="{confirm_link}" style="background: #222222; border: 15px solid #222222; font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol; font-size: 13px; line-height: 1.1; text-align: center; text-decoration: none; display: block; border-radius: 3px; font-weight: bold;" class="button-a">
<span style="color:#ffffff;" class="button-link">&nbsp;&nbsp;&nbsp;&nbsp;[[email:welcome.cta]]&nbsp;&nbsp;&nbsp;&nbsp;</span>
<span style="color:#ffffff;" class="button-link">[[email:welcome.cta]] &rarr;</span>
</a>
</td>
</tr>

@ -20,7 +20,7 @@
<tr>
<td style="border-radius: 3px; background: #222222; text-align: center;" class="button-td">
<a href="{confirm_link}" style="background: #222222; border: 15px solid #222222; font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol; font-size: 13px; line-height: 1.1; text-align: center; text-decoration: none; display: block; border-radius: 3px; font-weight: bold;" class="button-a">
<span style="color:#ffffff;" class="button-link">&nbsp;&nbsp;&nbsp;&nbsp;[[email:welcome.cta]]&nbsp;&nbsp;&nbsp;&nbsp;</span>
<span style="color:#ffffff;" class="button-link">[[email:welcome.cta]] &rarr;</span>
</a>
</td>
</tr>

@ -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) {

Loading…
Cancel
Save