Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enabled Topic Owners to properly delete posts in their topic #20

Merged
merged 7 commits into from
Feb 27, 2025
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@
</a>
</li>
{{{end}}}
<li {{{ if posts.deleted }}}hidden{{{ end }}}>
<a class="dropdown-item rounded-1 d-flex align-items-center gap-2" component="post/endorse" role="menuitem" href="#" class="{{{ if posts.deleted }}}hidden{{{ end }}}">
<span class="menu-icon"><i class="fa fa-star" style="color: goldenrod;"></i></span> Endorse
</a>
</li>
{{{end}}}

{{{ if !posts.deleted }}}
Expand Down
3 changes: 2 additions & 1 deletion src/privileges/posts.js
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ privsPosts.canDelete = async function (pid, uid) {
isMod: posts.isModerator([pid], uid),
isLocked: topics.isLocked(postData.tid),
isOwner: posts.isOwner(pid, uid),
isTopicOwner: topics.isOwner(postData.tid, uid),
'posts:delete': privsPosts.can('posts:delete', pid, uid),
});
results.isMod = results.isMod[0];
Expand All @@ -184,7 +185,7 @@ privsPosts.canDelete = async function (pid, uid) {
return { flag: false, message: `[[error:post-delete-duration-expired, ${meta.config.postDeleteDuration}]]` };
}
const { deleterUid } = postData;
const flag = results['posts:delete'] && ((results.isOwner && (deleterUid === 0 || deleterUid === postData.uid)) || results.isMod);
const flag = results['posts:delete'] && ((results.isOwner && (deleterUid === 0 || deleterUid === postData.uid)) || results.isMod || results.isTopicOwner);
return { flag: flag, message: '[[error:no-privileges]]' };
};

Expand Down
2 changes: 1 addition & 1 deletion src/socket.io/posts/tools.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ module.exports = function (SocketPosts) {
postData.display_purge_tools = results.canPurge;
postData.display_flag_tools = socket.uid && results.canFlag.flag;
postData.display_topic_owner_tools = socket.uid === topicsData.uid;
postData.display_moderator_tools = postData.display_edit_tools || postData.display_delete_tools;
postData.display_moderator_tools = results.isAdmin || results.isModerator;
postData.display_move_tools = results.isAdmin || results.isModerator;
postData.display_change_owner_tools = results.isAdmin || results.isModerator;
postData.display_ip_ban = (results.isAdmin || results.isGlobalMod) && !postData.selfPost;
Expand Down
56 changes: 56 additions & 0 deletions test/posts.js
Original file line number Diff line number Diff line change
Expand Up @@ -1215,3 +1215,59 @@ describe('Posts\'', async () => {
});
});
});

describe('Post deletion by topic owner', () => {
let topicOwnerUid;
let posterUid;
let cid;
let tid;
let mainPid;
let replyPid;

before(async () => {
topicOwnerUid = await user.create({ username: 'topicowner' });
posterUid = await user.create({ username: 'regularuser' });

({ cid } = await categories.create({
name: 'Test Category',
description: 'Test category for topic owner deletion tests',
}));

const topicData = await topics.post({
uid: topicOwnerUid,
cid: cid,
title: 'Test Topic for Owner Deletion Rights',
content: 'This is the main post of the topic',
});

tid = topicData.topicData.tid;
mainPid = topicData.postData.pid;

const replyData = await topics.reply({
uid: posterUid,
tid: tid,
content: 'This is a reply posted by another user',
});

replyPid = replyData.pid;
});

it('should allow topic owner to delete any post under their topic', async () => {
assert(await user.exists(topicOwnerUid), 'Topic owner user should exist');
assert(await user.exists(posterUid), 'Poster user should exist');

const isOwner = await topics.isOwner(tid, topicOwnerUid);
assert.strictEqual(isOwner, true, 'User should be the topic owner');

const isPostOwner = await posts.isOwner(replyPid, topicOwnerUid);
assert.strictEqual(isPostOwner, false, 'Topic owner should not be the post owner');

await apiPosts.delete({ uid: topicOwnerUid }, { pid: replyPid, tid: tid });

const isDeleted = await posts.getPostField(replyPid, 'deleted');
assert.strictEqual(isDeleted, 1, 'Post should be marked as deleted');

const postExists = await posts.exists(replyPid);
assert.strictEqual(postExists, true, 'Post should still exist in database after deletion');
});
});