mirror of
https://github.com/NodeBB/NodeBB.git
synced 2026-05-06 09:17:01 +02:00
Merge branch 'master' into develop
This commit is contained in:
@@ -108,7 +108,7 @@
|
||||
"nodebb-plugin-spam-be-gone": "2.3.2",
|
||||
"nodebb-plugin-web-push": "0.7.7",
|
||||
"nodebb-rewards-essentials": "1.0.2",
|
||||
"nodebb-theme-harmony": "2.2.61",
|
||||
"nodebb-theme-harmony": "2.2.62",
|
||||
"nodebb-theme-lavender": "7.1.21",
|
||||
"nodebb-theme-peace": "2.2.57",
|
||||
"nodebb-theme-persona": "14.2.33",
|
||||
|
||||
@@ -115,14 +115,20 @@ define('forum/category', [
|
||||
}
|
||||
|
||||
function handleDescription() {
|
||||
const fadeEl = document.querySelector(`.description.clamp-fade-sm-4`);
|
||||
if (!fadeEl) {
|
||||
return;
|
||||
}
|
||||
|
||||
fadeEl.addEventListener('click', () => {
|
||||
const state = fadeEl.classList.contains('line-clamp-4');
|
||||
fadeEl.classList.toggle('line-clamp-4', !state);
|
||||
const fadeEl = $(`.description[class*="clamp-fade-"]`);
|
||||
fadeEl.on('click', function () {
|
||||
const $this = $(this);
|
||||
let clampClass = $this.data('clampClass');
|
||||
if (!clampClass) {
|
||||
const match = $this.attr('class').match(/line-clamp-(\S+)/);
|
||||
if (match && match[1]) {
|
||||
clampClass = `line-clamp-${match[1]}`;
|
||||
fadeEl.data('clampClass', clampClass);
|
||||
}
|
||||
}
|
||||
if (clampClass) {
|
||||
fadeEl.toggleClass(clampClass);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -134,7 +134,7 @@ module.exports = function (User) {
|
||||
`uid:${uid}:flag:pids`,
|
||||
`uid:${uid}:sessions`,
|
||||
`uid:${uid}:shares`,
|
||||
`uid:${uid}:profile:images`,
|
||||
`uid:${uid}:profile:pictures`,
|
||||
`invitation:uid:${uid}`,
|
||||
];
|
||||
|
||||
|
||||
@@ -143,7 +143,7 @@ module.exports = function (User) {
|
||||
const filename = generateProfileImageFilename(updateUid, extension);
|
||||
const uploadedImage = await image.uploadImage(filename, `profile/uid-${updateUid}`, {
|
||||
uid: updateUid,
|
||||
path: picture.path,
|
||||
path: normalizedPath,
|
||||
name: 'profileAvatar',
|
||||
});
|
||||
|
||||
|
||||
@@ -125,7 +125,7 @@
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">[[admin/extend/plugins:order-active]]</h4>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-hidden="true"></button>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="[[global:buttons.close]]"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<div class="">
|
||||
<div component="toaster/tray" class="alert-window fixed-bottom mb-5 mb-md-2 me-2 me-md-5 ms-auto" style="width:300px; z-index: 1090;">
|
||||
<div id="reconnect-alert" class="alert alert-dismissible alert-warning fade hide" component="toaster/toast">
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-hidden="true"></button>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="[[global:buttons.close]]"></button>
|
||||
<p class="mb-0">[[global:reconnecting-message, {config.siteTitle}]]</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -45,12 +45,12 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="create-modal">
|
||||
<div class="modal fade" id="create-modal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">[[admin/manage/tags:create]]</h4>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-hidden="true"></button>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="[[global:buttons.close]]"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<div class="modal fade" id="create-modal">
|
||||
<div class="modal fade" id="create-modal" tabindex="-1" aria-label="[[admin/manage/groups:create]]">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">[[admin/manage/groups:create]]</h4>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-hidden="true"></button>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="[[global:buttons.close]]"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<div id="chat-modal" class="chat-modal d-flex flex-nowrap modal hide overflow-visible" tabindex="-1" role="dialog" aria-labelledby="Chat" aria-hidden="true" data-center="false">
|
||||
<div id="chat-modal" class="chat-modal d-flex flex-nowrap modal overflow-visible" tabindex="-1" role="dialog" aria-labelledby="chat-room-title-{roomId}" data-center="false">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content" component="chat/message/window">
|
||||
<div class="modal-header d-flex gap-4 justify-content-between">
|
||||
<div class="fs-6 flex-grow-1 fw-semibold tracking-tight text-truncate text-nowrap" component="chat/room/name" data-icon="{icon}">{{{ if ./roomName }}}<i class="fa {icon} text-muted"></i> {roomName}{{{ else }}}{./chatWithMessage}{{{ end}}}</div>
|
||||
<div id="chat-room-title-{roomId}" class="fs-6 flex-grow-1 fw-semibold tracking-tight text-truncate text-nowrap" component="chat/room/name" data-icon="{icon}">{{{ if ./roomName }}}<i class="fa {icon} text-muted"></i> {roomName}{{{ else }}}{./chatWithMessage}{{{ end}}}</div>
|
||||
<div class="d-flex gap-1 align-items-center">
|
||||
<button type="button" class="btn btn-ghost btn-sm d-none d-md-flex align-self-stretch align-items-center" data-action="maximize" title="[[modules:chat.maximize]]" data-bs-toggle="tooltip" data-bs-placement="bottom">
|
||||
<i class="fa fa-fw fa-expand text-muted"></i>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<div id="crop-picture-modal" class="modal" tabindex="-1" role="dialog" aria-labelledby="crop-picture" aria-hidden="true">
|
||||
<div id="crop-picture-modal" class="modal" tabindex="-1" role="dialog" aria-labelledby="crop-picture" aria-labelledby="crop-picture">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h3 id="crop-picture">[[user:crop-picture]]</h3>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-hidden="true"></button>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="[[global:buttons.close]]"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div id="upload-progress-box" class="progress hide">
|
||||
@@ -30,7 +30,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-outline-secondary" data-bs-dismiss="modal" aria-hidden="true">Close</button>
|
||||
<button class="btn btn-outline-secondary" data-bs-dismiss="modal">Close</button>
|
||||
<button class="btn btn-primary upload-btn {{{ if !allowSkippingCrop }}}hidden{{{ end }}}">[[user:upload-picture]]</button>
|
||||
<button class="btn btn-primary crop-btn">[[user:upload-cropped-picture]]</button>
|
||||
</div>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<div class="modal" tabindex="-1" role="dialog" aria-labelledby="[[flags:modal-title]]" aria-hidden="true">
|
||||
<div class="modal" tabindex="-1" role="dialog" aria-label="[[flags:modal-title]]">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">[[flags:modal-title]]</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-hidden="true"></button>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="[[global:buttons.close]]"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p class="lead">
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<div class="modal" tabindex="-1" role="dialog" aria-labelledby="upload-file" aria-hidden="true">
|
||||
<div class="modal" tabindex="-1" role="dialog" aria-label="{title}">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">{title}</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-hidden="true"></button>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="[[global:buttons.close]]"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form class="mb-3" id="uploadForm" action="" method="post" enctype="multipart/form-data">
|
||||
@@ -36,7 +36,7 @@
|
||||
<div id="alert-error" class="alert alert-danger hide"></div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-outline-secondary" data-bs-dismiss="modal" aria-hidden="true">[[global:close]]</button>
|
||||
<button class="btn btn-outline-secondary" data-bs-dismiss="modal">[[global:close]]</button>
|
||||
<button id="fileUploadSubmitBtn" class="btn btn-primary">{button}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
<div id="upload-picture-from-url-modal" class="modal" tabindex="-1" role="dialog" aria-labelledby="upload-picture-url" aria-hidden="true">
|
||||
<div id="upload-picture-from-url-modal" class="modal" tabindex="-1" role="dialog" aria-labelledby="upload-picture-url" aria-label="[[user:upload-picture]]">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="upload-picture-url">[[user:upload-picture]]</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-hidden="true"></button>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="[[global:buttons.close]]"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<input id="uploadFromUrl" class="form-control" type="text"/>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-outline-secondary" data-bs-dismiss="modal" aria-hidden="true">[[global:close]]</button>
|
||||
<button class="btn btn-outline-secondary" data-bs-dismiss="modal">[[global:close]]</button>
|
||||
<button class="btn btn-primary upload-btn">[[user:upload-picture]]</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<div id="reconnect-alert" class="alert alert-dismissible alert-warning fade hide" component="toaster/toast" role="alert">
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-hidden="true"></button>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="[[global:buttons.close]]"></button>
|
||||
<p class="mb-0">[[global:reconnecting-message, {config.siteTitle}]]</p>
|
||||
</div>
|
||||
24
test/user.js
24
test/user.js
@@ -1144,6 +1144,30 @@ describe('User', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should normalize uploaded image to png', async () => {
|
||||
const oldValue = meta.config['profile:convertProfileImageToPNG'];
|
||||
meta.config['profile:convertProfileImageToPNG'] = 1;
|
||||
|
||||
const uid = await User.create({ username: 'pngnormalize', password: '123456' });
|
||||
const { jar, csrf_token } = await helpers.loginUser('pngnormalize', '123456');
|
||||
const pathToJpeg = path.join(__dirname, '../test/files/normalise.jpg');
|
||||
|
||||
const { response } = await helpers.uploadFile(
|
||||
`${nconf.get('url')}/api/user/pngnormalize/uploadpicture`,
|
||||
pathToJpeg, { }, jar, csrf_token
|
||||
);
|
||||
assert.strictEqual(response.statusCode, 200);
|
||||
const picture = await db.getObjectField(`user:${uid}`, 'picture');
|
||||
const uploadedPath = path.join(
|
||||
nconf.get('upload_path'), `${picture.replace(nconf.get('upload_url'), '')}`
|
||||
);
|
||||
const sharp = require('sharp');
|
||||
const metadata = await sharp(uploadedPath).metadata();
|
||||
assert.strictEqual(metadata.format, 'png');
|
||||
|
||||
meta.config['profile:convertProfileImageToPNG'] = oldValue;
|
||||
});
|
||||
|
||||
it('should not allow image data with bad MIME type to be passed in', (done) => {
|
||||
User.uploadCroppedPicture({
|
||||
callerUid: uid,
|
||||
|
||||
Reference in New Issue
Block a user