mirror of
https://github.com/NodeBB/NodeBB.git
synced 2026-02-10 00:27:35 +01:00
Merge remote-tracking branch 'origin/develop' into activitypub
This commit is contained in:
@@ -29,7 +29,7 @@ editController.get = async function (req, res, next) {
|
||||
const [canUseSignature, canManageUsers, customUserFields] = await Promise.all([
|
||||
privileges.global.can('signature', req.uid),
|
||||
privileges.admin.can('admin:users', req.uid),
|
||||
accountHelpers.getCustomUserFields(userData),
|
||||
accountHelpers.getCustomUserFields(req.uid, userData),
|
||||
]);
|
||||
|
||||
userData.customUserFields = customUserFields;
|
||||
|
||||
@@ -139,7 +139,7 @@ helpers.getUserDataByUserSlug = async function (userslug, callerUID, query = {})
|
||||
return hookData.userData;
|
||||
};
|
||||
|
||||
helpers.getCustomUserFields = async function (userData) {
|
||||
helpers.getCustomUserFields = async function (callerUID, userData) {
|
||||
// Remote users' fields are serialized in hash
|
||||
if (!utils.isNumber(userData.uid)) {
|
||||
const customFields = await user.getUserField(userData.uid, 'customFields');
|
||||
@@ -165,9 +165,22 @@ helpers.getCustomUserFields = async function (userData) {
|
||||
const keys = await db.getSortedSetRange('user-custom-fields', 0, -1);
|
||||
const allFields = (await db.getObjects(keys.map(k => `user-custom-field:${k}`))).filter(Boolean);
|
||||
|
||||
const isSelf = String(callerUID) === String(userData.uid);
|
||||
const [isAdmin, isModOfAny] = await Promise.all([
|
||||
privileges.users.isAdministrator(callerUID),
|
||||
user.isModeratorOfAnyCategory(callerUID),
|
||||
]);
|
||||
|
||||
const fields = allFields.filter((field) => {
|
||||
const visibilityCheck = isAdmin || isModOfAny || isSelf || field.visibility === 'all' ||
|
||||
(
|
||||
field.visibility === 'loggedin' &&
|
||||
String(callerUID) !== '0' &&
|
||||
String(callerUID) !== '-1'
|
||||
);
|
||||
const minRep = field['min:rep'] || 0;
|
||||
return userData.reputation >= minRep || meta.config['reputation:disabled'];
|
||||
const repCheck = userData.reputation >= minRep || meta.config['reputation:disabled'];
|
||||
return visibilityCheck && repCheck;
|
||||
});
|
||||
|
||||
fields.forEach((f) => {
|
||||
|
||||
@@ -27,7 +27,7 @@ profileController.get = async function (req, res, next) {
|
||||
const [latestPosts, bestPosts, customUserFields] = await Promise.all([
|
||||
getLatestPosts(req.uid, userData),
|
||||
getBestPosts(req.uid, userData),
|
||||
accountHelpers.getCustomUserFields(userData),
|
||||
accountHelpers.getCustomUserFields(req.uid, userData),
|
||||
posts.parseSignature(userData, req.uid),
|
||||
]);
|
||||
userData.customUserFields = customUserFields;
|
||||
|
||||
@@ -310,6 +310,7 @@ usersController.customFields = async function (req, res) {
|
||||
field.selectOptionsFormatted = field['select-options'].trim().split('\n').join(', ');
|
||||
}
|
||||
field['min:rep'] = field['min:rep'] || 0;
|
||||
field.visibility = field.visibility || 'all';
|
||||
});
|
||||
res.render('admin/manage/users/custom-fields', { fields: fields });
|
||||
};
|
||||
|
||||
@@ -110,12 +110,16 @@ async function resizeImage(fileObj) {
|
||||
|
||||
await image.resizeImage({
|
||||
path: fileObj.path,
|
||||
target: file.appendToFileName(fileObj.path, '-resized'),
|
||||
target: meta.config.resizeImageKeepOriginal ?
|
||||
file.appendToFileName(fileObj.path, '-resized') :
|
||||
fileObj.path,
|
||||
width: meta.config.resizeImageWidth,
|
||||
quality: meta.config.resizeImageQuality,
|
||||
});
|
||||
// Return the resized version to the composer/postData
|
||||
fileObj.url = file.appendToFileName(fileObj.url, '-resized');
|
||||
if (meta.config.resizeImageKeepOriginal) {
|
||||
fileObj.url = file.appendToFileName(fileObj.url, '-resized');
|
||||
}
|
||||
|
||||
return fileObj;
|
||||
}
|
||||
|
||||
@@ -39,9 +39,9 @@ redisModule.init = async function (opts) {
|
||||
|
||||
redisModule.createSessionStore = async function (options) {
|
||||
const meta = require('../meta');
|
||||
const sessionStore = require('connect-redis').default;
|
||||
const { RedisStore } = require('connect-redis');
|
||||
const client = await connection.connect(options);
|
||||
const store = new sessionStore({
|
||||
const store = new RedisStore({
|
||||
client: client,
|
||||
ttl: meta.getSessionTTLSeconds(),
|
||||
});
|
||||
|
||||
@@ -122,7 +122,6 @@ image.stripEXIF = async function (path) {
|
||||
};
|
||||
|
||||
image.checkDimensions = async function (path) {
|
||||
const meta = require('./meta');
|
||||
const result = await image.size(path);
|
||||
|
||||
if (result.width > meta.config.rejectImageWidth || result.height > meta.config.rejectImageHeight) {
|
||||
|
||||
@@ -234,7 +234,7 @@ Messaging.getRecentChats = async (callerUid, uid, start, stop) => {
|
||||
|
||||
Messaging.generateUsernames = function (room, excludeUid) {
|
||||
const users = room.users.filter(u => u && parseInt(u.uid, 10) !== excludeUid);
|
||||
const usernames = users.map(u => u.username);
|
||||
const usernames = users.map(u => u.displayname);
|
||||
if (users.length > 3) {
|
||||
return translator.compile(
|
||||
'modules:chat.usernames-and-x-others',
|
||||
@@ -248,8 +248,8 @@ Messaging.generateUsernames = function (room, excludeUid) {
|
||||
Messaging.generateChatWithMessage = async function (room, callerUid, userLang) {
|
||||
const users = room.users.filter(u => u && parseInt(u.uid, 10) !== callerUid);
|
||||
const usernames = users.map(u => (utils.isNumber(u.uid) ?
|
||||
`<a href="${relative_path}/uid/${u.uid}">${u.username}</a>` :
|
||||
`<a href="${relative_path}/user/${u.username}">${u.username}</a>`));
|
||||
`<a href="${relative_path}/uid/${u.uid}">${u.displayname}</a>` :
|
||||
`<a href="${relative_path}/user/${u.username}">${u.displayname}</a>`));
|
||||
let compiled = '';
|
||||
if (!users.length) {
|
||||
return '[[modules:chat.no-users-in-room]]';
|
||||
|
||||
13
src/upgrades/3.12.0/resize-image-keep-original.js
Normal file
13
src/upgrades/3.12.0/resize-image-keep-original.js
Normal file
@@ -0,0 +1,13 @@
|
||||
/* eslint-disable no-await-in-loop */
|
||||
|
||||
'use strict';
|
||||
|
||||
const db = require('../../database');
|
||||
|
||||
module.exports = {
|
||||
name: 'Add setting for keeping original image after resize',
|
||||
timestamp: Date.UTC(2024, 11, 2),
|
||||
method: async function () {
|
||||
await db.setObjectField('config', 'resizeImageKeepOriginal', 1);
|
||||
},
|
||||
};
|
||||
@@ -21,13 +21,14 @@
|
||||
<th class="text-muted">[[admin/manage/user-custom-fields:key]]</th>
|
||||
<th class="text-muted">[[admin/manage/user-custom-fields:name]]</th>
|
||||
<th class="text-muted">[[admin/manage/user-custom-fields:type]]</th>
|
||||
<th class="text-muted">[[admin/manage/user-custom-fields:visibility]]</th>
|
||||
<th class="text-muted text-end">[[admin/manage/user-custom-fields:min-rep]]</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{{ each fields }}}
|
||||
<tr data-key="{./key}" data-name="{./name}" data-icon="{./icon}" data-type="{./type}" data-min-rep="{./min:rep}" data-select-options="{./select-options}" class="align-middle">
|
||||
<tr data-key="{./key}" data-name="{./name}" data-icon="{./icon}" data-type="{./type}" data-min-rep="{./min:rep}" data-select-options="{./select-options}" data-visibility="{./visibility}" class="align-middle">
|
||||
<td style="width: 32px;">
|
||||
<a href="#" component="sort/handle" class="btn btn-light btn-sm d-none d-md-block ui-sortable-handle" style="cursor:grab;"><i class="fa fa-arrows-up-down text-muted"></i></a>
|
||||
</td>
|
||||
@@ -41,6 +42,9 @@
|
||||
</div>
|
||||
{{{ end }}}
|
||||
</td>
|
||||
<td>
|
||||
{./visibility}
|
||||
</td>
|
||||
<td class="text-end">
|
||||
{./min:rep}
|
||||
</td>
|
||||
|
||||
@@ -29,6 +29,15 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">[[admin/manage/user-custom-fields:visibility]]</label>
|
||||
<select name="visibility" class="form-select">
|
||||
<option value="all">[[admin/manage/user-custom-fields:visibility-all]]</option>
|
||||
<option value="loggedin">[[admin/manage/user-custom-fields:visibility-loggedin]]</option>
|
||||
<option value="privileged">[[admin/manage/user-custom-fields:visibility-privileged]]</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">[[admin/manage/user-custom-fields:minimum-reputation]]</label>
|
||||
<input class="form-control" type="number" name="min:rep" value="{./min:rep}" placeholder="0">
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<label for="downvote:disabled" class="form-check-label">[[admin/settings/reputation:disable-down-voting]]</label>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="upvoteVisibility" class="form-check-label">[[admin/settings/reputation:upvote-visibility]]</label>
|
||||
<label for="upvoteVisibility" class="form-label">[[admin/settings/reputation:upvote-visibility]]</label>
|
||||
<select id="upvoteVisibility" data-field="upvoteVisibility" class="form-select">
|
||||
<option value="all">[[admin/settings/reputation:upvote-visibility-all]]</option>
|
||||
<option value="loggedin">[[admin/settings/reputation:upvote-visibility-loggedin]]</option>
|
||||
@@ -23,7 +23,7 @@
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label for="downvoteVisibility" class="form-check-label">[[admin/settings/reputation:downvote-visibility]]</label>
|
||||
<label for="downvoteVisibility" class="form-label">[[admin/settings/reputation:downvote-visibility]]</label>
|
||||
<select id="downvoteVisibility" data-field="downvoteVisibility" class="form-select">
|
||||
<option value="all">[[admin/settings/reputation:downvote-visibility-all]]</option>
|
||||
<option value="loggedin">[[admin/settings/reputation:downvote-visibility-loggedin]]</option>
|
||||
|
||||
@@ -46,6 +46,11 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-check form-switch mb-3">
|
||||
<input class="form-check-input" type="checkbox" id="resizeImageKeepOriginal" data-field="resizeImageKeepOriginal">
|
||||
<label for="resizeImageKeepOriginal" class="form-check-label">[[admin/settings/uploads:resize-image-keep-original]]</label>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label" for="resizeImageQuality">[[admin/settings/uploads:resize-image-quality]]</label>
|
||||
<input id="resizeImageQuality" type="text" class="form-control" value="60" data-field="resizeImageQuality" placeholder="60">
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
|
||||
<tr>
|
||||
<td style="padding: 40px 40px 6px 40px; font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol; font-size: 15px; line-height: 20px; color: #555555;">
|
||||
<h1 style="margin: 0; font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol; font-size: 24px; line-height: 27px; color: #333333; font-weight: normal;">[[email:greeting-with-name, {username}]]</h1>
|
||||
<h1 style="margin: 0; font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol; font-size: 24px; line-height: 27px; color: #333333; font-weight: normal;">[[email:greeting-with-name, {displayname}]]</h1>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -75,7 +75,7 @@
|
||||
<td style="padding: 6px 16px; font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol; width: 32px; vertical-align: middle;">{function.renderDigestAvatar}</td>
|
||||
<td style="padding: 6px 16px; font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol; line-height: 16px; color: #333333;">
|
||||
<p style="margin: 0;"><a style="text-decoration:none !important; text-decoration:none; color: #333333;" href="{url}/topic/{topTopics.slug}"><strong>{topTopics.title}</strong></a></p>
|
||||
<p style="margin: 0; font-size: 12px;"><a style="text-decoration:none !important; text-decoration:none; color: #aaaaaa; line-height: 16px;" href="{url}/uid/{topTopics.teaser.user.uid}"><strong>{topTopics.teaser.user.username}</strong></a></p>
|
||||
<p style="margin: 0; font-size: 12px;"><a style="text-decoration:none !important; text-decoration:none; color: #aaaaaa; line-height: 16px;" href="{url}/uid/{topTopics.teaser.user.uid}"><strong>{topTopics.teaser.user.displayname}</strong></a></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -110,7 +110,7 @@
|
||||
<td style="padding: 6px 16px; font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol; width: 32px; vertical-align: middle;">{function.renderDigestAvatar}</td>
|
||||
<td style="padding: 6px 16px; font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol; line-height: 16px; color: #333333;">
|
||||
<p style="margin: 0;"><a style="text-decoration:none !important; text-decoration:none; color: #333333;" href="{url}/topic/{popularTopics.slug}"><strong>{popularTopics.title}</strong></a></p>
|
||||
<p style="margin: 0; font-size: 12px;"><a style="text-decoration:none !important; text-decoration:none; color: #aaaaaa; line-height: 16px;" href="{url}/uid/{popularTopics.teaser.user.uid}"><strong>{popularTopics.teaser.user.username}</strong></a></p>
|
||||
<p style="margin: 0; font-size: 12px;"><a style="text-decoration:none !important; text-decoration:none; color: #aaaaaa; line-height: 16px;" href="{url}/uid/{popularTopics.teaser.user.uid}"><strong>{popularTopics.teaser.user.displayname}</strong></a></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -145,7 +145,7 @@
|
||||
<td style="padding: 6px 16px; font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol; width: 32px; vertical-align: middle;">{function.renderDigestAvatar}</td>
|
||||
<td style="padding: 6px 16px; font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol; line-height: 16px; color: #333333;">
|
||||
<p style="margin: 0;"><a style="text-decoration:none !important; text-decoration:none; color: #333333;" href="{url}/topic/{recent.slug}"><strong>{recent.title}</strong></a></p>
|
||||
<p style="margin: 0; font-size: 12px;"><a style="text-decoration:none !important; text-decoration:none; color: #aaaaaa; line-height: 16px;" href="{url}/uid/{recent.teaser.user.uid}"><strong>{recent.teaser.user.username}</strong></a></p>
|
||||
<p style="margin: 0; font-size: 12px;"><a style="text-decoration:none !important; text-decoration:none; color: #aaaaaa; line-height: 16px;" href="{url}/uid/{recent.teaser.user.uid}"><strong>{recent.teaser.user.displayname}</strong></a></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<li component="chat/message" class="chat-message mx-2 pe-2 {{{ if messages.deleted }}} deleted{{{ end }}} {{{ if messages.pinned}}} pinned{{{ end }}} {{{ if messages.newSet }}}border-top pt-3{{{ end }}}" data-mid="{messages.messageId}" data-uid="{messages.fromuid}" data-index="{messages.index}" data-self="{messages.self}" data-break="{messages.newSet}" data-timestamp="{messages.timestamp}" data-username="{messages.fromUser.username}">
|
||||
<li component="chat/message" class="chat-message mx-2 pe-2 {{{ if messages.deleted }}} deleted{{{ end }}} {{{ if messages.pinned}}} pinned{{{ end }}} {{{ if messages.newSet }}}border-top pt-3{{{ end }}}" data-mid="{messages.messageId}" data-uid="{messages.fromuid}" data-index="{messages.index}" data-self="{messages.self}" data-break="{messages.newSet}" data-timestamp="{messages.timestamp}" data-username="{messages.fromUser.username}" data-displayname="{messages.fromUser.displayname}">
|
||||
|
||||
{{{ if messages.parent }}}
|
||||
<!-- IMPORT partials/chats/parent.tpl -->
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
{{{ if ./teaser }}}
|
||||
<div class="teaser-content text-sm line-clamp-3 text-break">
|
||||
{buildAvatar(./teaser.user, "14px", true, "align-middle")}
|
||||
<strong class="text-xs fw-semibold teaser-username">{./teaser.user.username}:</strong>
|
||||
<strong class="text-xs fw-semibold teaser-username">{./teaser.user.displayname}:</strong>
|
||||
{./teaser.content}
|
||||
</div>
|
||||
<div class="teaser-timestamp text-muted text-xs">{{{ if ./teaser.timeagoLong }}}{./teaser.timeagoLong}{{{ else }}}<span class="timeago" title="{./teaser.timestampISO}"></span>{{{ end }}}</div>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<a data-index="{./index}" data-uid="{./uid}" class="btn btn-ghost btn-sm d-flex ff-secondary d-flex justify-content-start align-items-center gap-2 {{{ if ./online }}}online{{{ end}}}" href="{config.relative_path}/uid/{./uid}">
|
||||
<div>{buildAvatar(users, "24px", true)}</div>
|
||||
<div class="d-flex gap-1 flex-grow-1 text-nowrap text-truncate">
|
||||
<span component="chat/user/list/username" class="text-truncate">{./username}</span>
|
||||
<span component="chat/user/list/username" class="text-truncate">{./displayname}</span>
|
||||
{{{ if ./isOwner }}}<span><i class="fa fa-star text-warning" data-bs-toggle="tooltip" title="[[modules:chat.owner]]"></i></span>{{{ end }}}
|
||||
</div>
|
||||
</a>
|
||||
|
||||
Reference in New Issue
Block a user