Skip to content

Commit

Permalink
Merge pull request #18 from CMU-313/US4_endorse_topic_post
Browse files Browse the repository at this point in the history
US4: Completed back-end logic for "Endorse Topic" button that appears upon clicking the "Topic Tools" drop-down menu
  • Loading branch information
tanoctavius authored Oct 10, 2024
2 parents 284ad83 + 17c67f9 commit 9addcdb
Show file tree
Hide file tree
Showing 26 changed files with 254 additions and 10 deletions.
Binary file added dump.rdb
Binary file not shown.
1 change: 1 addition & 0 deletions public/language/en-GB/error.json
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@
"cant-flag-privileged": "You are not allowed to flag the profiles or content of privileged users (moderators/global moderators/admins)",
"cant-locate-flag-report": "Cannot locate flag report",
"self-vote": "You cannot vote on your own post",
"self-endorse": "You cannot endorse your own topic",
"too-many-upvotes-today": "You can only upvote %1 times a day",
"too-many-upvotes-today-user": "You can only upvote a user %1 times a day",
"too-many-downvotes-today": "You can only downvote %1 times a day",
Expand Down
7 changes: 7 additions & 0 deletions public/language/en-GB/topic.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"share": "Share",
"tools": "Tools",
"locked": "Locked",
"endorsed": "Endorsed",
"pinned": "Pinned",
"pinned-with-expiry": "Pinned until %1",
"scheduled": "Scheduled",
Expand All @@ -55,6 +56,10 @@
"user-locked-topic-on": "%1 locked this topic on %2",
"user-unlocked-topic-ago": "%1 unlocked this topic %2",
"user-unlocked-topic-on": "%1 unlocked this topic on %2",
"user-endorsed-topic-on": "%1 endorsed this topic on %2",
"user-endorsed-topic-ago": "%1 endorsed this topic %2",
"user-unendorsed-topic-on": "%1 unendorsed this topic on %2",
"user-unendorsed-topic-ago": "%1 unendorsed this topic %2",
"user-pinned-topic-ago": "%1 pinned this topic %2",
"user-pinned-topic-on": "%1 pinned this topic on %2",
"user-unpinned-topic-ago": "%1 unpinned this topic %2",
Expand Down Expand Up @@ -112,6 +117,8 @@
"thread-tools.unpin": "Unpin Topic",
"thread-tools.lock": "Lock Topic",
"thread-tools.unlock": "Unlock Topic",
"thread-tools.endorse": "Endorse Topic",
"thread-tools.unendorse": "Unendorse Topic",
"thread-tools.move": "Move Topic",
"thread-tools.move-posts": "Move Posts",
"thread-tools.move-all": "Move All",
Expand Down
3 changes: 2 additions & 1 deletion public/language/en-US/error.json
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@
"cant-flag-privileged": "You are not allowed to flag the profiles or content of privileged users (moderators/global moderators/admins)",
"cant-locate-flag-report": "Cannot locate flag report",
"self-vote": "You cannot vote on your own post",
"self-endorse": "You cannot endorse your own topic",
"too-many-upvotes-today": "You can only upvote %1 times a day",
"too-many-upvotes-today-user": "You can only upvote a user %1 times a day",
"too-many-downvotes-today": "You can only downvote %1 times a day",
Expand Down Expand Up @@ -239,4 +240,4 @@
"api.501": "The route you are trying to call is not implemented yet, please try again tomorrow",
"api.503": "The route you are trying to call is not currently available due to a server configuration",
"api.reauth-required": "The resource you are trying to access requires (re-)authentication."
}
}
10 changes: 8 additions & 2 deletions public/language/en-US/topic.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"share": "Share",
"tools": "Tools",
"locked": "Locked",
"endorsed": "Endorsed",
"pinned": "Pinned",
"pinned-with-expiry": "Pinned until %1",
"scheduled": "Scheduled",
Expand All @@ -49,7 +50,10 @@
"user-locked-topic-ago": "%1 locked this topic %2",
"user-locked-topic-on": "%1 locked this topic on %2",
"user-unlocked-topic-ago": "%1 unlocked this topic %2",
"user-unlocked-topic-on": "%1 unlocked this topic on %2",
"user-endorsed-topic-on": "%1 endorsed this topic on %2",
"user-endorsed-topic-ago": "%1 endorsed this topic %2",
"user-unendorsed-topic-on": "%1 unendorsed this topic on %2",
"user-unendorsed-topic-ago": "%1 unendorsed this topic %2",
"user-pinned-topic-ago": "%1 pinned this topic %2",
"user-pinned-topic-on": "%1 pinned this topic on %2",
"user-unpinned-topic-ago": "%1 unpinned this topic %2",
Expand Down Expand Up @@ -99,6 +103,8 @@
"thread-tools.unpin": "Unpin Topic",
"thread-tools.lock": "Lock Topic",
"thread-tools.unlock": "Unlock Topic",
"thread-tools.endorse": "Endorse Topic",
"thread-tools.unendorse": "Unendorse Topic",
"thread-tools.move": "Move Topic",
"thread-tools.move-posts": "Move Posts",
"thread-tools.move-all": "Move All",
Expand Down Expand Up @@ -217,4 +223,4 @@
"post-tools": "Post tools",
"unread-posts-link": "Unread posts link",
"thumb-image": "Topic thumbnail image"
}
}
5 changes: 4 additions & 1 deletion public/openapi/components/schemas/TopicObject.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,9 @@ TopicObjectSlim:
type: string
locked:
type: number
endorsed:
type: number
description: Whether or not this particular topic is endorsed by admin
pinned:
type: number
description: Whether or not this particular topic is pinned to the top of the
Expand Down Expand Up @@ -283,4 +286,4 @@ TopicObjectSlim:
type: number
description: The number of thumbnails associated with this topic
required:
- tid
- tid
2 changes: 1 addition & 1 deletion public/openapi/read/admin/extend/plugins.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -205,4 +205,4 @@ get:
type: number
version:
type: string
- $ref: ../../../components/schemas/CommonProps.yaml#/CommonProps
- $ref: ../../../components/schemas/CommonProps.yaml#/CommonProps
4 changes: 3 additions & 1 deletion public/openapi/read/tags/tag.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ get:
type: number
locked:
type: number
endorsed:
type: number
pinned:
type: number
description: Whether or not this particular topic is pinned to the top of the
Expand Down Expand Up @@ -263,4 +265,4 @@ get:
- categories
- $ref: ../../components/schemas/Pagination.yaml#/Pagination
- $ref: ../../components/schemas/Breadcrumbs.yaml#/Breadcrumbs
- $ref: ../../components/schemas/CommonProps.yaml#/CommonProps
- $ref: ../../components/schemas/CommonProps.yaml#/CommonProps
4 changes: 3 additions & 1 deletion public/openapi/write.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,8 @@ paths:
$ref: 'write/topics/tid/state.yaml'
/topics/{tid}/lock:
$ref: 'write/topics/tid/lock.yaml'
/topics/{tid}/endorse:
$ref: 'write/topics/tid/endorse.yaml'
/topics/{tid}/pin:
$ref: 'write/topics/tid/pin.yaml'
/topics/{tid}/follow:
Expand Down Expand Up @@ -261,4 +263,4 @@ paths:
/files/:
$ref: 'write/files.yaml'
/files/folder:
$ref: 'write/files/folder.yaml'
$ref: 'write/files/folder.yaml'
53 changes: 53 additions & 0 deletions public/openapi/write/topics/tid/endorse.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
put:
tags:
- topics
summary: endorse a topic
description: This operation endorses an existing topic.
parameters:
- in: path
name: tid
schema:
type: string
required: true
description: a valid topic id
example: 1
responses:
'200':
description: Topic successfully endorsed
content:
application/json:
schema:
type: object
properties:
status:
$ref: ../../../components/schemas/Status.yaml#/Status
response:
type: object
properties: {}
delete:
tags:
- topics
summary: unendorsed a topic
description: This operation unendorses a topic.
parameters:
- in: path
name: tid
schema:
type: string
required: true
description: a valid topic id
example: 1
responses:
'200':
description: Topic successfully unendorsed
content:
application/json:
schema:
type: object
properties:
status:
$ref: ../../../components/schemas/Status.yaml#/Status
response:
type: object
properties: {}

10 changes: 10 additions & 0 deletions public/src/client/category/tools.js
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,10 @@ define('forum/category/tools', [
socket.on('event:topic_purged', onTopicPurged);
socket.on('event:topic_locked', setLockedState);
socket.on('event:topic_unlocked', setLockedState);
// Comment @YG
// Listening for the event to be triggered, and setting state of endorsement accordingly.
// socket.on('event:topic_endorsed', setEndorsedState);
// socket.on('event:topic_unendorsed', setEndorsedState);
socket.on('event:topic_pinned', setPinnedState);
socket.on('event:topic_unpinned', setPinnedState);
socket.on('event:topic_moved', onTopicMoved);
Expand Down Expand Up @@ -180,6 +184,8 @@ define('forum/category/tools', [
socket.removeListener('event:topic_purged', onTopicPurged);
socket.removeListener('event:topic_locked', setLockedState);
socket.removeListener('event:topic_unlocked', setLockedState);
// socket.removeListener('event:topic_endorsed', setEndorsedState);
// socket.removeListener('event:topic_unendorsed', setEndorsedState);
socket.removeListener('event:topic_pinned', setPinnedState);
socket.removeListener('event:topic_unpinned', setPinnedState);
socket.removeListener('event:topic_moved', onTopicMoved);
Expand Down Expand Up @@ -211,6 +217,7 @@ define('forum/category/tools', [
const areAllDeleted = areAll(isTopicDeleted, tids);
const isAnyPinned = isAny(isTopicPinned, tids);
const isAnyLocked = isAny(isTopicLocked, tids);
// const isAnyEndorsed = isAny(isTopicEndorsed, tids);
const isAnyScheduled = isAny(isTopicScheduled, tids);
const areAllScheduled = areAll(isTopicScheduled, tids);

Expand All @@ -221,6 +228,9 @@ define('forum/category/tools', [
components.get('topic/lock').toggleClass('hidden', isAnyLocked);
components.get('topic/unlock').toggleClass('hidden', !isAnyLocked);

// components.get('topic/endorse').toggleClass('hidden', isAnyEndorsed);
// components.get('topic/unendorse').toggleClass('hidden', !isAnyEndorsed);

components.get('topic/pin').toggleClass('hidden', areAllScheduled || isAnyPinned);
components.get('topic/unpin').toggleClass('hidden', areAllScheduled || !isAnyPinned);

Expand Down
6 changes: 6 additions & 0 deletions public/src/client/topic/events.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ define('forum/topic/events', [
'event:topic_locked': threadTools.setLockedState,
'event:topic_unlocked': threadTools.setLockedState,

// Comment @YG
// Define endorsed event.
// TODO: why do we need to define events?
'event:topic_endorsed': threadTools.setEndorsedState,
'event:topic_unendorsed': threadTools.setEndorsedState,

'event:topic_pinned': threadTools.setPinnedState,
'event:topic_unpinned': threadTools.setPinnedState,

Expand Down
35 changes: 35 additions & 0 deletions public/src/client/topic/threadTools.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,18 @@ define('forum/topic/threadTools', [
return false;
});

// Comment @YG
// Handles the front-end logic that establishes connection with APIs through topicCommand()
topicContainer.on('click', '[component="topic/endorse"]', function () {
topicCommand('put', '/endorse', 'endorse');
return false;
});

topicContainer.on('click', '[component="topic/unendorse"]', function () {
topicCommand('del', '/endorse', 'unendorse');
return false;
});

topicContainer.on('click', '[component="topic/pin"]', function () {
topicCommand('put', '/pin', 'pin');
return false;
Expand Down Expand Up @@ -313,13 +325,36 @@ define('forum/topic/threadTools', [

threadEl.find('[component="post"][data-uid="' + app.user.uid + '"].deleted [component="post/tools"]').toggleClass('hidden', isLocked);

// Comment @YG
// The $ is an alias for the jQuery() function to manipulate HTML element and data.
// This operation looks for element with the component and add hidden if data.isLocked.
$('[component="topic/labels"] [component="topic/locked"]').toggleClass('hidden', !data.isLocked);
// Finds dropdown-menu elem inside post/tools elem and set its HTML to be empty.
$('[component="post/tools"] .dropdown-menu').html('');
ajaxify.data.locked = data.isLocked;

posts.addTopicEvents(data.events);
};

// Comment @YG
// Event handler that responds to API calls triggered by clicking on the topicContainer.
ThreadTools.setEndorsedState = function (data) {
const threadEl = components.get('topic');
if (parseInt(data.tid, 10) !== parseInt(threadEl.attr('data-tid'), 10)) {
return;
}

components.get('topic/endorse').toggleClass('hidden', data.isEndorsed).parent().attr('hidden', data.isEndorsed ? '' : null);
components.get('topic/unendorse').toggleClass('hidden', !data.isEndorsed).parent().attr('hidden', !data.isEndorsed ? '' : null);

$('[component="topic/labels"] [component="topic/locked"]').toggleClass('hidden', !data.isLocked);
$('[component="topic/endorsed"]').toggleClass('hidden', !data.isEndorsed);

ajaxify.data.endorsed = data.isEndorsed;
posts.addTopicEvents(data.events);
};


ThreadTools.setDeleteState = function (data) {
const threadEl = components.get('topic');
if (parseInt(data.tid, 10) !== parseInt(threadEl.attr('data-tid'), 10)) {
Expand Down
1 change: 1 addition & 0 deletions src/api/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ exports.doTopicAction = async function (action, event, caller, { tids }) {

await Promise.all(tids.map(async (tid) => {
const title = await topics.getTopicField(tid, 'title');
// A generic call that handles toggling actions.
const data = await topics.tools[action](tid, caller.uid);
const notifyUids = await privileges.categories.filterUids('topics:read', data.cid, uids);
socketHelpers.emitToUids(event, data, notifyUids);
Expand Down
14 changes: 14 additions & 0 deletions src/api/topics.js
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,20 @@ topicsAPI.unlock = async function (caller, data) {
});
};

// Comment @YG
// Handles front-end responses passed in by methods in threadTools.js and triggers backend func calls.
topicsAPI.endorse = async function (caller, data) {
await doTopicAction('endorse', 'event:topic_endorsed', caller, {
tids: data.tids,
});
};

topicsAPI.unendorse = async function (caller, data) {
await doTopicAction('unendorse', 'event:topic_unendorsed', caller, {
tids: data.tids,
});
};

topicsAPI.follow = async function (caller, data) {
await topics.follow(data.tid, caller.uid);
};
Expand Down
13 changes: 13 additions & 0 deletions src/controllers/write/topics.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,19 @@ Topics.unlock = async (req, res) => {
helpers.formatApiResponse(200, res);
};

// Comment @YG
// Configured http response after clicking the 'endorse' button.
Topics.endorse = async (req, res) => {
await api.topics.endorse(req, { tids: [req.params.tid] });
helpers.formatApiResponse(200, res);
};

Topics.unendorse = async (req, res) => {
await api.topics.unendorse(req, { tids: [req.params.tid] });
helpers.formatApiResponse(200, res);
};
// End of implementation

Topics.follow = async (req, res) => {
await api.topics.follow(req, req.params);
helpers.formatApiResponse(200, res);
Expand Down
1 change: 1 addition & 0 deletions src/database/redis/hash.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ module.exports = function (module) {

module.setObjectField = async function (key, field, value) {
if (!field) {
console.log('No field detected.\n');
return;
}
if (Array.isArray(key)) {
Expand Down
7 changes: 7 additions & 0 deletions src/posts/tools.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ module.exports = function (Posts) {
Posts.tools = {};

Posts.tools.delete = async function (uid, pid) {
// Comment @YG
// I actually don't know the difference between this function and
// the one in "delete.js" as both of them seemed to be called when a
// post get deleted. However, this function is called first.
return await togglePostDelete(uid, pid, true);
};

Expand All @@ -14,6 +18,9 @@ module.exports = function (Posts) {
};

async function togglePostDelete(uid, pid, isDelete) {
// Comment @YG
// This function will be called whenever you confirm your deletion
// through the "delete" button for each post.
const [postData, canDelete] = await Promise.all([
Posts.getPostData(pid),
privileges.posts.canDelete(pid, uid),
Expand Down
1 change: 0 additions & 1 deletion src/privileges/topics.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ privsTopics.get = async function (tid, uid) {
const editable = isAdminOrMod;
const deletable = (privData['topics:delete'] && (isOwner || isModerator)) || isAdministrator;
const mayReply = privsTopics.canViewDeletedScheduled(topicData, {}, false, privData['topics:schedule']);

return await plugins.hooks.fire('filter:privileges.topics.get', {
'topics:reply': (privData['topics:reply'] && ((!topicData.locked && mayReply) || isModerator)) || isAdministrator,
'topics:read': privData['topics:read'] || isAdministrator,
Expand Down
Loading

0 comments on commit 9addcdb

Please sign in to comment.