Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions public/openapi/components/schemas/TopicObject.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,10 @@ TopicObjectSlim:
type: number
postercount:
type: number
# Instructed to make this addition by ChatGPT, code by copilot autocomplete
# Add resolve field to topic schema
resolve:
type: number
scheduled:
type: number
deleted:
Expand Down
4 changes: 4 additions & 0 deletions public/openapi/write.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,10 @@ paths:
$ref: 'write/topics/tid/thumbs/order.yaml'
/topics/{tid}/events:
$ref: 'write/topics/tid/events.yaml'
# Instructed to add by ChatGPT, code written by copilot autocomplete
# Set up path to resolve topic endpoint
/topics/{tid}/resolve:
$ref: 'write/topics/tid/resolve.yaml'
/topics/{tid}/events/{eventId}:
$ref: 'write/topics/tid/events/eventId.yaml'
/posts/{pid}:
Expand Down
46 changes: 46 additions & 0 deletions public/openapi/write/topics/tid/resolve.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# File instructed to write by ChatGPT, code written by ChatGPT
# defines an API endpoint for resolving a topic
put:
tags:
- topics
summary: Resolve a topic
description: Marks a topic as resolved.
operationId: resolveTopic
parameters:
- in: path
name: tid
required: true
schema:
type: number
description: The topic identifier that is to be marked as resolved.
requestBody:
content:
application/json:
schema:
type: object
properties:
resolved:
type: boolean
description: A boolean flag to indicate the resolve status. True to mark the topic as resolved, false to mark it as unresolved.
required:
- resolved
responses:
'200':
description: Topic successfully resolved
content:
application/json:
schema:
type: object
properties:
success:
type: boolean
message:
type: string
'400':
description: Bad request. Possible reasons: missing parameters or incorrect values.
'404':
description: Topic not found.
'401':
description: Unauthorized. User is not logged in or lacks the necessary permissions to resolve the topic.
'500':
description: Internal server error. An error occurred while attempting to resolve the topic.
26 changes: 26 additions & 0 deletions public/src/client/topic.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ define('forum/topic', [
addRepliesHandler();
addPostsPreviewHandler();

// instructed to add by ChatGPT, written by ChatGPT
// call handling function to handle click of resolve button
handleResolveButton();

handleBookmark(tid);

$(window).on('scroll', utils.debounce(updateTopicTitle, 250));
Expand All @@ -72,6 +76,28 @@ define('forum/topic', [
hooks.fire('action:topic.loaded', ajaxify.data);
};

// instructed to add by ChatGPT, written by ChatGPT
// handle click events for resolving topics
function handleResolveButton() {
// attach event listener to the resolve button
$(document).on('click', '[component="topic/resolve"]', function() {
// Sends a PUT request to the server to mark a topic, identified by 'tid', as resolved.
// The resolve parameter is set to 1, indicating the action to resolve the topic.
api.put('/topics/' + tid + '/resolve', { resolve: 1 })
.then(function(response) {
// Upon successful resolution, refreshes the page to reflect changes.
location.reload();
})
.catch(function(error) {
// If the PUT request fails, displays an error message to the user.
// It uses a custom alert system to show the error message or a default message if none is provided.
alerts.error(error.message || 'Failed to update topic resolution status.');
});
});
}



function handleTopicSearch() {
require(['mousetrap'], (mousetrap) => {
if (config.topicSearchEnabled) {
Expand Down
12 changes: 12 additions & 0 deletions src/controllers/write/topics.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,18 @@ Topics.create = async (req, res) => {
}
};

// Code instructed to write and written by ChatGPT
// Function to resolve a topic
Topics.resolve = async (req, res) => {
// Call resolve tool to mark topic as resolved
// It takes the topic ID (tid) from the request parameters and the user ID (uid) from the request object.
await topics.tools.resolve(req.params.tid, req.uid);
// Send a success response to the client.
// It formats the response as per the API's standard, with a 200 OK status code.
helpers.formatApiResponse(200, res);
};


Topics.reply = async (req, res) => {
const id = await lockPosting(req, '[[error:already-posting]]');
try {
Expand Down
3 changes: 2 additions & 1 deletion src/privileges/topics.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ privsTopics.get = async function (tid, uid) {
'posts:view_deleted': privData['posts:view_deleted'] || isAdministrator,
read: privData.read || isAdministrator,
purge: (privData.purge && (isOwner || isModerator)) || isAdministrator,

// add privilege for resolve topics, only topic owner, admin, or mod
can_resolve: isOwner || isAdminOrMod,
view_thread_tools: editable || deletable,
editable: editable,
deletable: deletable,
Expand Down
2 changes: 1 addition & 1 deletion src/routes/write/topics.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ module.exports = function () {
setupApiRoute(router, 'get', '/:tid', [], controllers.write.topics.get);
setupApiRoute(router, 'post', '/:tid', [middleware.checkRequired.bind(null, ['content']), middleware.assert.topic], controllers.write.topics.reply);
setupApiRoute(router, 'delete', '/:tid', [...middlewares], controllers.write.topics.purge);

setupApiRoute(router, 'put', '/:tid/resolve', [...middlewares], controllers.write.topics.resolve);
setupApiRoute(router, 'put', '/:tid/state', [...middlewares], controllers.write.topics.restore);
setupApiRoute(router, 'delete', '/:tid/state', [...middlewares], controllers.write.topics.delete);

Expand Down
5 changes: 4 additions & 1 deletion src/topics/create.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ module.exports = function (Topics) {
postcount: 0,
viewcount: 0,
isAnonymous: isAnonymous, // store anonymous status
resolve: 0, // Add resolve field to topic's data, defaults to 0 for unresolved (1 for resolved)
};

if (Array.isArray(data.tags) && data.tags.length) {
Expand Down Expand Up @@ -226,7 +227,9 @@ module.exports = function (Topics) {
topicInfo,
] = await Promise.all([
posts.getUserInfoForPosts([postData.uid], uid),
Topics.getTopicFields(tid, ['tid', 'uid', 'title', 'slug', 'cid', 'postcount', 'mainPid', 'scheduled']),
// Instructed to add resolve field by ChatGPT
// Add resolve field to topic's field getter
Topics.getTopicFields(tid, ['tid', 'uid', 'resolve', 'title', 'slug', 'cid', 'postcount', 'mainPid', 'scheduled']),
Topics.addParentPosts([postData]),
Topics.syncBacklinks(postData),
posts.parsePost(postData),
Expand Down
4 changes: 3 additions & 1 deletion src/topics/data.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ const utils = require('../utils');
const translator = require('../translator');
const plugins = require('../plugins');

// Instructed to add by ChatGPT
// Add resolve field to int fields of topic
const intFields = [
'tid', 'cid', 'uid', 'mainPid', 'postcount',
'viewcount', 'postercount', 'deleted', 'locked', 'pinned',
'pinExpiry', 'timestamp', 'upvotes', 'downvotes', 'lastposttime',
'deleterUid',
'deleterUid','resolve',
];

module.exports = function (Topics) {
Expand Down
22 changes: 22 additions & 0 deletions src/topics/tools.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,28 @@ module.exports = function (Topics) {
return await toggleDelete(tid, uid, false);
};

// Code instructed and written by ChatGPT
// Resolves a topic after checking for topic existence and privileges
topicTools.resolve = async function (tid, uid) {
const topicData = await topics.getTopicFields(tid, ['uid']);
// check if topic exists
if (!topicData) {
throw new Error('[[error:no-topic]]');
}
const isOwner = parseInt(topicData.uid, 10) === parseInt(uid, 10);
const isAdmin = await user.isAdministrator(uid);
const isMod = await user.isModerator(uid, tid); // Assuming tid can be used to determine the forum/category for moderator check
// Check if the user has the 'topics:can_resolve' privilege on the topic, or if they are the owner, an admin, or a moderator
const canResolve = isOwner || isAdmin || isMod;
if (!canResolve) {
throw new Error('[[error:no-privileges]]');
}
// Proceed to mark the topic as resolved since the user has the required privilege or role
await topics.setTopicField(tid, 'resolve', 1);
topicData.resolve = 1;
return topicData;
};

async function toggleDelete(tid, uid, isDelete) {
const topicData = await Topics.getTopicData(tid);
if (!topicData) {
Expand Down
22 changes: 22 additions & 0 deletions themes/nodebb-theme-persona/templates/partials/post_bar.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,26 @@
<!-- IMPORT partials/thread_tools.tpl -->
</div>
<!-- IMPORT partials/topic/reply-button.tpl -->

<!-- Code written by ChatGPT -->
<!-- Resolve button only available to users with correct privileges to resolve the topic -->
<!-- IF privileges.can_resolve -->
<button
component="topic/resolve"
class="btn btn-sm btn-default"
type="button"
{{{if resolve}}}disabled{{{end}}}>
<!-- Button content changes based on resolution state -->
{{{if resolve}}}
<!-- Displayed if the topic is already resolved -->
<i class="fa fa-fw fa-check"></i>
<span class="visible-sm-inline visible-md-inline visible-lg-inline">Already Resolved</span>
{{{else}}}
<!-- Displayed if the topic is not resolved yet -->
<i class="fa"></i>
<span class="visible-sm-inline visible-md-inline visible-lg-inline">Mark as Resolved</span>
{{{end}}}
</button>
<!-- ENDIF privileges.can_resolve -->

</div>
6 changes: 6 additions & 0 deletions themes/nodebb-theme-persona/templates/topic.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@
{{{each icons}}}{@value}{{{end}}}
</span>
<span component="topic/title">{title}</span>
<!-- Code written by ChatGPT -->
<!-- IF resolve -->
<small style="text-align: right; color: green;">Resolved</small>
<!-- ELSE -->
<small style="text-align: right; color: red;">Unresolved</small>
<!-- ENDIF resolve -->
</span>
</h1>

Expand Down