mirror of
https://github.com/NodeBB/NodeBB.git
synced 2026-05-06 13:56:09 +02:00
Merge branch 'bootstrap5' of https://github.com/NodeBB/NodeBB into bootstrap5
This commit is contained in:
@@ -77,7 +77,7 @@
|
||||
"jquery-ui": "1.13.2",
|
||||
"jsesc": "3.0.2",
|
||||
"json2csv": "5.0.7",
|
||||
"jsonwebtoken": "8.5.1",
|
||||
"jsonwebtoken": "9.0.0",
|
||||
"less": "4.1.3",
|
||||
"lodash": "4.17.21",
|
||||
"logrotate-stream": "0.2.8",
|
||||
@@ -131,7 +131,7 @@
|
||||
"slideout": "1.0.1",
|
||||
"socket.io": "4.5.4",
|
||||
"socket.io-client": "4.5.4",
|
||||
"@socket.io/redis-adapter": "8.0.0",
|
||||
"@socket.io/redis-adapter": "8.0.1",
|
||||
"sortablejs": "1.15.0",
|
||||
"spdx-license-list": "6.6.0",
|
||||
"spider-detector": "2.0.0",
|
||||
@@ -151,7 +151,7 @@
|
||||
"zxcvbn": "4.4.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@apidevtools/swagger-parser": "10.0.3",
|
||||
"@apidevtools/swagger-parser": "9.0.0",
|
||||
"@commitlint/cli": "17.4.1",
|
||||
"@commitlint/config-angular": "17.4.0",
|
||||
"coveralls": "3.1.1",
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
"headers.coep-help": "Khi được bật (mặc định), sẽ đặt tiêu đề thành <code>require-corp</code>",
|
||||
"headers.coop": "Cross-Origin-Opener-Policy",
|
||||
"headers.corp": "Cross-Origin-Resource-Policy",
|
||||
"headers.permissions-policy": "Permissions-Policy",
|
||||
"headers.permissions-policy": "Quyền-Chính sách",
|
||||
"headers.permissions-policy-help": "Allows setting permissions policy header, for example \"geolocation=*, camera=()\", see <a href=\"https://github.com/w3c/webappsec-permissions-policy/blob/main/permissions-policy-explainer.md\">this</a> for more info.",
|
||||
"hsts": "Bảo Vệ Truyền Tải Nghiêm Ngặt",
|
||||
"hsts.enabled": "Đã bật HSTS (đề nghị)",
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"from-help": "Tên người gửi hiển thị trong email.",
|
||||
|
||||
"confirmation-settings": "Xác nhận",
|
||||
"confirmation.expiry": "Hours to keep email confirmation link valid",
|
||||
"confirmation.expiry": "Số giờ để giữ cho liên kết xác nhận email hợp lệ",
|
||||
|
||||
"smtp-transport": "Truyền Tải SMTP",
|
||||
"smtp-transport.enabled": "Bật truyền tải SMTP",
|
||||
|
||||
@@ -27,5 +27,5 @@
|
||||
"flags.action-on-resolve": "Do the following when a flag is resolved",
|
||||
"flags.action-on-reject": "Do the following when a flag is rejected",
|
||||
"flags.action.nothing": "Do nothing",
|
||||
"flags.action.rescind": "Rescind the notification send to moderators/administrators"
|
||||
"flags.action.rescind": "Hủy bỏ gửi thông báo cho người điều hành/quản trị viên"
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"authentication": "Xác thực",
|
||||
"email-confirm-interval": "Người dùng không thể gửi lại email xác nhận cho đến khi",
|
||||
"email-confirm-interval2": "minutes have elapsed",
|
||||
"email-confirm-interval2": "phút đã trôi qua",
|
||||
"allow-login-with": "Cho phép đăng nhập với",
|
||||
"allow-login-with.username-email": "Tên Đăng Nhập hoặc Email",
|
||||
"allow-login-with.username": "Chỉ Tên Đăng Nhập",
|
||||
@@ -29,8 +29,8 @@
|
||||
"session-time-days": "Ngày",
|
||||
"session-time-seconds": "Giây",
|
||||
"session-time-help": "Giá trị này dùng để điều chỉnh thời gian người dùng đăng nhập khi họ chọn "Nhớ Tôi" lúc đăng nhập. Lưu ý chỉ một trong những giá trị này sẽ được dùng. Nếu không có giá trị <i>giây</i> chúng tôi sẽ dùng <i>ngày</i>. Nếu không có <i>ngày</i> mặc định là <i>14 ngày</i>.",
|
||||
"session-duration": "Session length if \"Remember Me\" is not checked (seconds)",
|
||||
"session-duration-help": "By default — or if set to <code>0</code> — a user will stay logged in for the duration of the session (e.g. however long the browser window/tab remains open). Set this value to explicitly invalidate the session after the specified number of seconds.",
|
||||
"session-duration": "Thời lượng phiên nếu \"Ghi nhớ tôi\" không được chọn (giây)",
|
||||
"session-duration-help": "Theo mặc định — hoặc nếu đặt thành <code>0</code> — người dùng sẽ duy trì trạng thái đăng nhập trong suốt thời gian của phiên (VD: cửa sổ/tab trình duyệt vẫn mở trong bao lâu). Đặt giá trị này để vô hiệu hóa rõ ràng phiên sau số giây đã chỉ định.",
|
||||
"online-cutoff": "Số phút sau khi người dùng được coi là không hoạt động",
|
||||
"online-cutoff-help": "Nếu người dùng không thao tác trong khoảng thời gian này, được coi là không hoạt động và không nhận được cập nhật theo thời gian thực.",
|
||||
"registration": "Đăng Ký Người Dùng",
|
||||
|
||||
@@ -16,6 +16,57 @@ get:
|
||||
responses:
|
||||
'200':
|
||||
description: user emails successfully listed
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
status:
|
||||
$ref: ../../../components/schemas/Status.yaml#/Status
|
||||
response:
|
||||
type: object
|
||||
properties:
|
||||
emails:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
description: An email address
|
||||
post:
|
||||
tags:
|
||||
- users
|
||||
summary: add email to user
|
||||
description: |
|
||||
This operation adds an email to the user account, optionally bypassing the confirmation step if requested.
|
||||
|
||||
**Note**: The confirmation bypass can only be called by super administrators or users with the `admin:users` privilege.
|
||||
parameters:
|
||||
- in: path
|
||||
name: uid
|
||||
schema:
|
||||
type: integer
|
||||
required: true
|
||||
description: uid of the account to add the email
|
||||
example: 1
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
email:
|
||||
type: string
|
||||
description: A single email address
|
||||
example: test@example.org
|
||||
skipConfirmation:
|
||||
type: boolean
|
||||
description: If truthy, will automatically confirm the user's email.
|
||||
example: 1
|
||||
required:
|
||||
- email
|
||||
responses:
|
||||
'200':
|
||||
description: email successfully added to user account
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
|
||||
@@ -307,18 +307,17 @@ async function isPrivilegedOrSelfAndPasswordMatch(caller, data) {
|
||||
async function processDeletion({ uid, method, password, caller }) {
|
||||
const isTargetAdmin = await user.isAdministrator(uid);
|
||||
const isSelf = parseInt(uid, 10) === parseInt(caller.uid, 10);
|
||||
const isAdmin = await user.isAdministrator(caller.uid);
|
||||
const hasAdminPrivilege = await privileges.admin.can('admin:users', caller.uid);
|
||||
|
||||
if (isSelf && meta.config.allowAccountDelete !== 1) {
|
||||
throw new Error('[[error:account-deletion-disabled]]');
|
||||
} else if (!isSelf && !isAdmin) {
|
||||
} else if (!isSelf && !hasAdminPrivilege) {
|
||||
throw new Error('[[error:no-privileges]]');
|
||||
} else if (isTargetAdmin) {
|
||||
throw new Error('[[error:cant-delete-admin]');
|
||||
}
|
||||
|
||||
// Privilege checks -- only deleteAccount is available for non-admins
|
||||
const hasAdminPrivilege = await privileges.admin.can('admin:users', caller.uid);
|
||||
if (!hasAdminPrivilege && ['delete', 'deleteContent'].includes(method)) {
|
||||
throw new Error('[[error:no-privileges]]');
|
||||
}
|
||||
|
||||
@@ -253,6 +253,24 @@ Users.getInviteGroups = async function (req, res) {
|
||||
return helpers.formatApiResponse(200, res, userInviteGroups.map(group => group.displayName));
|
||||
};
|
||||
|
||||
Users.addEmail = async (req, res) => {
|
||||
const canManageUsers = await privileges.admin.can('admin:users', req.uid);
|
||||
const skipConfirmation = canManageUsers && req.body.skipConfirmation;
|
||||
|
||||
if (skipConfirmation) {
|
||||
await user.setUserField(req.params.uid, 'email', req.body.email);
|
||||
await user.email.confirmByUid(req.params.uid);
|
||||
} else {
|
||||
await api.users.update(req, {
|
||||
uid: req.params.uid,
|
||||
email: req.body.email,
|
||||
});
|
||||
}
|
||||
|
||||
const emails = await db.getSortedSetRangeByScore('email:uid', 0, 500, req.params.uid, req.params.uid);
|
||||
helpers.formatApiResponse(200, res, { emails });
|
||||
};
|
||||
|
||||
Users.listEmails = async (req, res) => {
|
||||
const [isPrivileged, { showemail }] = await Promise.all([
|
||||
user.isPrivileged(req.uid),
|
||||
|
||||
@@ -66,6 +66,7 @@ privsAdmin.routeMap = {
|
||||
uploadDefaultAvatar: 'admin:settings',
|
||||
};
|
||||
privsAdmin.routePrefixMap = {
|
||||
'dashboard/': 'admin:dashboard',
|
||||
'manage/categories/': 'admin:categories',
|
||||
'manage/privileges/': 'admin:privileges',
|
||||
'manage/groups/': 'admin:groups',
|
||||
|
||||
@@ -48,6 +48,7 @@ function authenticatedRoutes() {
|
||||
setupApiRoute(router, 'get', '/:uid/invites/groups', [...middlewares, middleware.assert.user], controllers.write.users.getInviteGroups);
|
||||
|
||||
setupApiRoute(router, 'get', '/:uid/emails', [...middlewares, middleware.assert.user], controllers.write.users.listEmails);
|
||||
setupApiRoute(router, 'post', '/:uid/emails', [...middlewares, middleware.assert.user], controllers.write.users.addEmail);
|
||||
setupApiRoute(router, 'get', '/:uid/emails/:email', [...middlewares, middleware.assert.user], controllers.write.users.getEmail);
|
||||
setupApiRoute(router, 'post', '/:uid/emails/:email/confirm', [...middlewares, middleware.assert.user], controllers.write.users.confirmEmail);
|
||||
|
||||
|
||||
@@ -826,7 +826,7 @@ describe('Categories', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe.only('Categories.getModeratorUids', () => {
|
||||
describe('Categories.getModeratorUids', () => {
|
||||
let cid;
|
||||
|
||||
before(async () => {
|
||||
@@ -865,7 +865,7 @@ describe('Categories', () => {
|
||||
const payload = {};
|
||||
payload[cid] = { disabled: 1 };
|
||||
await Categories.update(payload);
|
||||
const uids = await Categories.getModeratorUids([1, 2]);
|
||||
const uids = await Categories.getModeratorUids([cid, 2]);
|
||||
assert(!uids[0].includes('1'));
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user