mirror of
https://github.com/NodeBB/NodeBB.git
synced 2026-07-28 17:30:50 +02:00
Merge branch 'master' into develop
This commit is contained in:
@@ -108,7 +108,7 @@
|
||||
"nodebb-plugin-spam-be-gone": "2.3.0",
|
||||
"nodebb-plugin-web-push": "0.7.2",
|
||||
"nodebb-rewards-essentials": "1.0.0",
|
||||
"nodebb-theme-harmony": "2.0.14",
|
||||
"nodebb-theme-harmony": "2.0.16",
|
||||
"nodebb-theme-lavender": "7.1.17",
|
||||
"nodebb-theme-peace": "2.2.38",
|
||||
"nodebb-theme-persona": "14.0.12",
|
||||
|
||||
@@ -50,6 +50,12 @@ body.page-user-chats {
|
||||
}
|
||||
}
|
||||
|
||||
[component="chat/message/parent"] {
|
||||
[component="chat/message/parent/content"] > p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.expanded-chat {
|
||||
.chat-content {
|
||||
.message-body {
|
||||
|
||||
@@ -264,15 +264,28 @@ define('forum/topic', [
|
||||
}
|
||||
|
||||
function addParentHandler() {
|
||||
components.get('topic').on('click', '[component="post/parent"]', function (e) {
|
||||
const toPid = $(this).attr('data-topid');
|
||||
|
||||
function gotoPost(event, toPid) {
|
||||
const toPost = $('[component="topic"]>[component="post"][data-pid="' + toPid + '"]');
|
||||
if (toPost.length) {
|
||||
e.preventDefault();
|
||||
event.preventDefault();
|
||||
navigator.scrollToIndex(toPost.attr('data-index'), true);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
components.get('topic').on('click', '[component="post/parent"]', function (e) {
|
||||
const parentEl = $(this);
|
||||
const contentEl = parentEl.find('[component="post/parent/content"]');
|
||||
if (contentEl.length) {
|
||||
contentEl.toggleClass('line-clamp-1');
|
||||
parentEl.find('.timeago').toggleClass('hidden');
|
||||
parentEl.toggleClass('flex-column').toggleClass('flex-row');
|
||||
} else {
|
||||
return gotoPost(e, parentEl.attr('data-topid'));
|
||||
}
|
||||
});
|
||||
|
||||
components.get('topic').on('click', '[component="post/parent"] .timeago', function (e) {
|
||||
return gotoPost(e, $(this).parents('[data-parent-pid]').attr('data-parent-pid'));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -298,7 +311,7 @@ define('forum/topic', [
|
||||
destroyed = true;
|
||||
}
|
||||
$(window).one('action:ajaxify.start', destroyTooltip);
|
||||
$('[component="topic"]').on('mouseenter', '[component="post/parent"], [component="post/content"] a, [component="topic/event"] a', async function () {
|
||||
$('[component="topic"]').on('mouseenter', 'a[component="post/parent"], [component="post/content"] a, [component="topic/event"] a', async function () {
|
||||
const link = $(this);
|
||||
destroyed = false;
|
||||
|
||||
|
||||
@@ -159,6 +159,17 @@ define('forum/topic/events', [
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const parentEl = $(`[component="post/parent"][data-parent-pid="${data.post.pid}"]`);
|
||||
if (parentEl.length) {
|
||||
parentEl.find('[component="post/parent/content"]').html(
|
||||
translator.unescape(data.post.content)
|
||||
);
|
||||
parentEl.find('img:not(.not-responsive)').addClass('img-fluid');
|
||||
parentEl.find('[component="post/parent/content]" img:not(.emoji)').each(function () {
|
||||
images.wrapImageInLink($(this));
|
||||
});
|
||||
}
|
||||
} else {
|
||||
hooks.fire('action:posts.edited', data);
|
||||
}
|
||||
@@ -185,26 +196,39 @@ define('forum/topic/events', [
|
||||
require(['forum/topic/replies'], function (replies) {
|
||||
replies.onPostPurged(postData);
|
||||
});
|
||||
$(`[component="post/parent"][data-parent-pid="${postData.pid}"]`).remove();
|
||||
}
|
||||
|
||||
function togglePostDeleteState(data) {
|
||||
const postEl = components.get('post', 'pid', data.pid);
|
||||
|
||||
if (!postEl.length) {
|
||||
return;
|
||||
const { isAdminOrMod } = ajaxify.data.privileges;
|
||||
const isSelfPost = String(data.uid) === String(app.user.uid);
|
||||
const isDeleted = !!data.deleted;
|
||||
if (postEl.length) {
|
||||
postEl.toggleClass('deleted');
|
||||
postTools.toggle(data.pid, isDeleted);
|
||||
|
||||
if (!isAdminOrMod && !isSelfPost) {
|
||||
postEl.find('[component="post/tools"]').toggleClass('hidden', isDeleted);
|
||||
if (isDeleted) {
|
||||
postEl.find('[component="post/content"]').translateHtml('[[topic:post-is-deleted]]');
|
||||
} else {
|
||||
postEl.find('[component="post/content"]').html(translator.unescape(data.content));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
postEl.toggleClass('deleted');
|
||||
const isDeleted = postEl.hasClass('deleted');
|
||||
postTools.toggle(data.pid, isDeleted);
|
||||
|
||||
if (!ajaxify.data.privileges.isAdminOrMod && parseInt(data.uid, 10) !== parseInt(app.user.uid, 10)) {
|
||||
postEl.find('[component="post/tools"]').toggleClass('hidden', isDeleted);
|
||||
if (isDeleted) {
|
||||
postEl.find('[component="post/content"]').translateHtml('[[topic:post-is-deleted]]');
|
||||
} else {
|
||||
postEl.find('[component="post/content"]').html(translator.unescape(data.content));
|
||||
}
|
||||
const parentEl = $(`[component="post/parent"][data-parent-pid="${data.pid}"]`);
|
||||
if (parentEl.length) {
|
||||
parentEl.each((i, el) => {
|
||||
const $parent = $(el);
|
||||
if (isDeleted) {
|
||||
$parent.find('[component="post/parent/content"]').translateHtml('[[topic:post-is-deleted]]');
|
||||
} else {
|
||||
$parent.find('[component="post/parent/content"]').html(translator.unescape(data.content));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ define('forum/topic/replies', ['forum/topic/posts', 'hooks', 'alerts', 'api'], f
|
||||
'downvote:disabled': ajaxify.data['downvote:disabled'],
|
||||
'reputation:disabled': ajaxify.data['reputation:disabled'],
|
||||
loggedIn: !!app.user.uid,
|
||||
hideParent: true,
|
||||
hideReplies: config.hasOwnProperty('showNestedReplies') ? !config.showNestedReplies : true,
|
||||
};
|
||||
app.parseAndTranslate('topic', 'posts', tplData, async function (html) {
|
||||
|
||||
@@ -137,7 +137,7 @@ module.exports = function (Messaging) {
|
||||
parentMids = parentMids.filter((mid, idx) => canView[idx]);
|
||||
|
||||
const parentMessages = await Messaging.getMessagesFields(parentMids, [
|
||||
'fromuid', 'content', 'timestamp', 'deleted',
|
||||
'mid', 'fromuid', 'content', 'timestamp', 'deleted',
|
||||
]);
|
||||
const parentUids = _.uniq(parentMessages.map(msg => msg && msg.fromuid));
|
||||
const usersMap = _.zipObject(
|
||||
|
||||
@@ -28,7 +28,7 @@ module.exports = function (Posts) {
|
||||
deleted: isDeleting ? 1 : 0,
|
||||
deleterUid: isDeleting ? uid : 0,
|
||||
});
|
||||
const postData = await Posts.getPostFields(pid, ['pid', 'tid', 'uid', 'content', 'timestamp']);
|
||||
const postData = await Posts.getPostFields(pid, ['pid', 'tid', 'uid', 'content', 'timestamp', 'deleted']);
|
||||
const topicData = await topics.getTopicFields(postData.tid, ['tid', 'cid', 'pinned']);
|
||||
postData.cid = topicData.cid;
|
||||
await Promise.all([
|
||||
|
||||
@@ -45,7 +45,7 @@ privsPosts.get = async function (pids, uid) {
|
||||
|
||||
const privileges = cids.map((cid, i) => {
|
||||
const isAdminOrMod = results.isAdmin || isModerator[cid];
|
||||
const editable = (privData['posts:edit'][cid] && (results.isOwner[i] || results.isModerator)) || results.isAdmin;
|
||||
const editable = (privData['posts:edit'][cid] && (results.isOwner[i] || results.isModerator[i])) || results.isAdmin;
|
||||
const viewDeletedPosts = results.isOwner[i] || privData['posts:view_deleted'][cid] || results.isAdmin;
|
||||
const viewHistory = results.isOwner[i] || privData['posts:history'][cid] || results.isAdmin;
|
||||
|
||||
|
||||
@@ -233,7 +233,7 @@ module.exports = function (Topics) {
|
||||
posts.getUserInfoForPosts([postOwner], uid),
|
||||
]);
|
||||
await Promise.all([
|
||||
Topics.addParentPosts([postData]),
|
||||
Topics.addParentPosts([postData], uid),
|
||||
Topics.syncBacklinks(postData),
|
||||
Topics.markAsRead([tid], uid),
|
||||
]);
|
||||
|
||||
@@ -12,6 +12,7 @@ const meta = require('../meta');
|
||||
const activitypub = require('../activitypub');
|
||||
const plugins = require('../plugins');
|
||||
const utils = require('../utils');
|
||||
const privileges = require('../privileges');
|
||||
|
||||
const backlinkRegex = new RegExp(`(?:${nconf.get('url').replace('/', '\\/')}|\b|\\s)\\/topic\\/(\\d+)(?:\\/\\w+)?`, 'g');
|
||||
|
||||
@@ -129,7 +130,7 @@ module.exports = function (Topics) {
|
||||
getPostUserData('uid', async uids => await posts.getUserInfoForPosts(uids, uid)),
|
||||
getPostUserData('editor', async uids => await user.getUsersFields(uids, ['uid', 'username', 'userslug'])),
|
||||
getPostReplies(postData, uid),
|
||||
Topics.addParentPosts(postData),
|
||||
Topics.addParentPosts(postData, uid),
|
||||
]);
|
||||
|
||||
postData.forEach((postObj, i) => {
|
||||
@@ -178,7 +179,7 @@ module.exports = function (Topics) {
|
||||
});
|
||||
};
|
||||
|
||||
Topics.addParentPosts = async function (postData) {
|
||||
Topics.addParentPosts = async function (postData, callerUid) {
|
||||
let parentPids = postData
|
||||
.filter(p => p && p.hasOwnProperty('toPid') && (activitypub.helpers.isUri(p.toPid) || utils.isNumber(p.toPid)))
|
||||
.map(postObj => postObj.toPid);
|
||||
@@ -187,16 +188,40 @@ module.exports = function (Topics) {
|
||||
return;
|
||||
}
|
||||
parentPids = _.uniq(parentPids);
|
||||
const parentPosts = await posts.getPostsFields(parentPids, ['uid']);
|
||||
const postPrivileges = await privileges.posts.get(parentPids, callerUid);
|
||||
const pidToPrivs = _.zipObject(parentPids, postPrivileges);
|
||||
|
||||
parentPids = parentPids.filter(p => pidToPrivs[p]['topics:read']);
|
||||
const parentPosts = await posts.getPostsFields(parentPids, ['uid', 'pid', 'timestamp', 'content', 'deleted']);
|
||||
const parentUids = _.uniq(parentPosts.map(postObj => postObj && postObj.uid));
|
||||
const userData = await user.getUsersFields(parentUids, ['username']);
|
||||
const userData = await user.getUsersFields(parentUids, ['username', 'userslug', 'picture']);
|
||||
|
||||
const usersMap = _.zipObject(parentUids, userData);
|
||||
|
||||
await Promise.all(parentPosts.map(async (parentPost) => {
|
||||
const postPrivs = pidToPrivs[parentPost.pid];
|
||||
if (parentPost.deleted && String(parentPost.uid) !== String(callerUid, 10) && !postPrivs['posts:view_deleted']) {
|
||||
parentPost.content = `<p>[[topic:post-is-deleted]]</p>`;
|
||||
return;
|
||||
}
|
||||
const foundPost = postData.find(p => String(p.pid) === String(parentPost.pid));
|
||||
if (foundPost) {
|
||||
parentPost.content = foundPost.content;
|
||||
return;
|
||||
}
|
||||
parentPost = await posts.parsePost(parentPost);
|
||||
}));
|
||||
|
||||
const parents = {};
|
||||
parentPosts.forEach((post, i) => {
|
||||
if (usersMap[post.uid]) {
|
||||
parents[parentPids[i]] = {
|
||||
uid: post.uid,
|
||||
pid: post.pid,
|
||||
content: post.content,
|
||||
user: usersMap[post.uid],
|
||||
timestamp: post.timestamp,
|
||||
timestampISO: post.timestampISO,
|
||||
username: usersMap[post.uid].username,
|
||||
displayname: usersMap[post.uid].displayname,
|
||||
};
|
||||
|
||||
@@ -8,6 +8,6 @@
|
||||
</div>
|
||||
<span class="chat-timestamp text-muted timeago text-nowrap hidden" title="{messages.parent.timestampISO}"></span>
|
||||
</div>
|
||||
<div component="chat/message/parent/content" class="text-muted line-clamp-1 w-100">{messages.parent.content}</div>
|
||||
<div component="chat/message/parent/content" class="text-muted line-clamp-1 text-break w-100">{messages.parent.content}</div>
|
||||
</div>
|
||||
</div>
|
||||
12
src/views/partials/topic/post-parent.tpl
Normal file
12
src/views/partials/topic/post-parent.tpl
Normal file
@@ -0,0 +1,12 @@
|
||||
<div component="post/parent" data-parent-pid="{./parent.pid}" data-uid="{./parent.uid}" class="btn btn-ghost btn-sm d-flex gap-2 text-start flex-row mb-2" style="font-size: 13px;">
|
||||
<div class="d-flex gap-2 text-nowrap">
|
||||
<div><i class="fa fa-sm fa-reply opacity-50"></i></div>
|
||||
<div class="d-flex flex-nowrap gap-1 align-items-center">
|
||||
<a href="{config.relative_path}/user/{./parent.user.userslug}" class="text-decoration-none lh-1">{buildAvatar(./parent.user, "14px", true, "not-responsive align-middle")}</a>
|
||||
<a class="fw-semibold" href="{config.relative_path}/user/{./parent.user.userslug}">{./parent.user.displayname}</a>
|
||||
</div>
|
||||
|
||||
<a href="{config.relative_path}/post/{./parent.pid}" class="text-muted timeago text-nowrap hidden" title="{./parent.timestampISO}"></a>
|
||||
</div>
|
||||
<div component="post/parent/content" class="text-muted line-clamp-1 text-break w-100">{./parent.content}</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user