diff --git a/core/plugins/groups/forum/assets/css/like.css b/core/plugins/groups/forum/assets/css/like.css
new file mode 100644
index 00000000000..10592a9eadc
--- /dev/null
+++ b/core/plugins/groups/forum/assets/css/like.css
@@ -0,0 +1,29 @@
+/* Should put this in the comments.less file */
+.comment-body a.like {
+ float: right;
+ color: gray;
+ margin-bottom: 10px;
+}
+
+.comment-body a.userLiked {
+ color: red;
+}
+
+.elementToHover {
+ display: inline-block;
+ position: relative;
+}
+
+.elementToPopup {
+ display: none;
+ position: absolute;
+ top: 30px;
+ left: -80px;
+ background-color: #555;
+ color: #fff;
+ padding: 10px;
+ border-radius: 5px;
+ z-index: 1;
+ width: 151px;
+ text-align: right;
+}
\ No newline at end of file
diff --git a/core/plugins/groups/forum/assets/js/like.js b/core/plugins/groups/forum/assets/js/like.js
new file mode 100644
index 00000000000..01f361b2c72
--- /dev/null
+++ b/core/plugins/groups/forum/assets/js/like.js
@@ -0,0 +1,121 @@
+console.log("like on GROUP forum");
+
+window.addEventListener('DOMContentLoaded', (domEvent) => {
+ // Find all the "like" button
+ const likeButton = document.querySelectorAll('.comment-body .like')
+ if (likeButton.length) {
+ for(let i = 0; i < likeButton.length;i++) {
+ likeButton[i].onclick = (e) => {
+ e.preventDefault();
+
+ let hasHeart = likeButton[i].classList.contains("userLiked");
+
+ const threadId = likeButton[i].dataset.thread;
+ const postId = likeButton[i].dataset.post;
+ const userId = likeButton[i].dataset.user;
+ const userName = likeButton[i].dataset.userName;
+ const likesList = likeButton[i].dataset.likesList;
+ const likeCount = likeButton[i].dataset.count;
+
+ console.log(threadId, postId, userId, likeCount, userName, likesList);
+
+ const likesListArray = likesList.split("/");
+
+ if (hasHeart) {
+ removeLike(threadId, postId, userId).then((res) => {
+ if (res.ok) {
+ const newLikeCount = Number(likeCount) - 1;
+ const newLikesString = likesListArray.filter(e => e !== userName).join('/');
+
+ // Create ELEMENT
+ const element = document.createElement('span');
+ element.classList.add("elementToPopup");
+ element.innerHTML = newLikesString.split("/").join("
");
+
+ likeButton[i].dataset.count = `${newLikeCount}`;
+ likeButton[i].innerHTML = (newLikeCount === 0) ? 'Like' : `Like (${newLikeCount})`;
+ likeButton[i].appendChild(element);
+ likeButton[i].classList.remove("userLiked");
+ likeButton[i].dataset.likesList = newLikesString;
+
+ console.warn(`Like removed for forum thread '${threadId}' of post '${postId}' for user ${userId}`);
+ }
+ })
+ } else {
+ addLike(threadId, postId, userId).then((res) => {
+ if (res.ok) {
+ const newLikeCount = Number(likeCount) + 1;
+ const newLikesString = [...likesListArray, userName].join('/');
+
+ // Create ELEMENT
+ const element = document.createElement('span');
+ element.classList.add("elementToPopup");
+ element.innerHTML = newLikesString.split("/").join("
");
+
+ likeButton[i].dataset.count = `${newLikeCount}`;
+ likeButton[i].innerHTML = `Like (${newLikeCount})`;
+ likeButton[i].appendChild(element);
+ likeButton[i].classList.add("userLiked");
+ likeButton[i].dataset.likesList = newLikesString;
+
+ console.log(`Like recorded for forum thread '${threadId}' of post '${postId}' for user ${userId}`);
+ }
+ })
+ }
+
+ return false;
+ };
+
+ // Hover over and mouse leave
+ likeButton[i].onmouseover = (e) => {
+ likeButton[i].getElementsByClassName('elementToPopup')[0].style.display = 'block';
+ };
+
+ likeButton[i].onmouseleave = (e) => {
+ likeButton[i].getElementsByClassName('elementToPopup')[0].style.display = 'none';
+ };
+ }
+ }
+});
+
+const addLike = async (threadId, postId, userId) => {
+ const postUrl = "/api/forum/likes/addLikeToPost";
+ const data = {threadId, postId, userId};
+
+ try {
+ let response = await fetch(postUrl, {
+ method: "POST", headers: {"Content-Type": "application/x-www-form-urlencoded"},
+ body: new URLSearchParams(data) // urlencoded form body
+ });
+
+ if (!response.ok) {
+ window.confirm("Server Error with API");
+ console.error(`Error Code: ${response.status} / Error Message: ${response.statusText}`);
+ }
+
+ return response;
+ } catch (error) {
+ if (error instanceof SyntaxError) {
+ console.error('There was a SyntaxError', error);
+ } else {
+ console.error('There was an error', error);
+ }
+ }
+};
+
+const removeLike = async (threadId, postId, userId) => {
+ const deleteAssertionUrl = "/api/forum/likes/deleteLikeFromPost";
+ const data = {threadId, postId, userId};
+
+ const deleteAssertionResp = await fetch(deleteAssertionUrl, {
+ method: "DELETE", headers: {"Content-Type": "application/x-www-form-urlencoded"},
+ body: new URLSearchParams(data)
+ })
+
+ if (!deleteAssertionResp.ok) {
+ window.confirm("Server Error with API");
+ console.error(`Error Code: ${response.status} / Error Message: ${response.statusText}`);
+ }
+
+ return deleteAssertionResp;
+}
\ No newline at end of file
diff --git a/core/plugins/groups/forum/forum.php b/core/plugins/groups/forum/forum.php
index 9dfb97f3d89..3642e7c0d84 100644
--- a/core/plugins/groups/forum/forum.php
+++ b/core/plugins/groups/forum/forum.php
@@ -1246,6 +1246,15 @@ public function threads()
$this->_authorize('thread', $thread->get('id'));
$this->_authorize('post');
+ // Get all the likes of this thread
+ $db = \App::get('db');
+ $queryLikes = "SELECT LIKES.threadId as 'threadId', LIKES.postId as 'postId',
+ LIKES.userId as 'userId', USERS.name as 'userName', USERS.email as 'userEmail'
+ FROM jos_forum_posts_like as LIKES, jos_users AS USERS
+ WHERE LIKES.userId = USERS.id AND LIKES.threadId = " . $thread->get('id');
+ $db->setQuery($queryLikes);
+ $initialLikesList = $db->loadObjectList();
+
// If the access is anything beyond public,
// make sure they're logged in.
if (User::isGuest() && !in_array($thread->get('access'), User::getAuthorisedViewLevels()))
@@ -1280,6 +1289,7 @@ public function threads()
->set('section', $section)
->set('category', $category)
->set('thread', $thread)
+ ->set('likes', $initialLikesList)
->set('filters', $filters)
->setErrors($this->getErrors())
->loadTemplate();
diff --git a/core/plugins/groups/forum/views/threads/tmpl/_comment.php b/core/plugins/groups/forum/views/threads/tmpl/_comment.php
index 73811a2382a..e23930959d2 100644
--- a/core/plugins/groups/forum/views/threads/tmpl/_comment.php
+++ b/core/plugins/groups/forum/views/threads/tmpl/_comment.php
@@ -7,6 +7,24 @@
defined('_HZEXEC_') or die();
+$this->css('like.css')
+ ->js('like.js');
+
+ $likeArray = $this->like;
+ $countLike = count($likeArray);
+ $currentUserId = User::get('id');
+
+ $userLikesComment = false;
+ $userNameLikesArray = "";
+ foreach ( $likeArray as $likeObj ) {
+ if ( $currentUserId == $likeObj->userId ) {
+ $userLikesComment = true;
+ }
+
+ $userNameLikesArray .= "/" . ($likeObj->userName);
+ }
+ $userNameLikesArray = substr($userNameLikesArray,1);
+
$this->comment->set('section', $this->filters['section']);
$this->comment->set('category', $this->category->get('alias'));
@@ -60,6 +78,25 @@