Skip to content
Permalink
Browse files

Add social_enable_likes_messages conf setting - refs BT#15393

Allows to user add likes or dislikes to posts in social wall. Requires DB changes:

```sql
CREATE TABLE message_likes (id BIGINT AUTO_INCREMENT NOT NULL, message_id BIGINT NOT NULL, user_id INT NOT NULL, liked TINYINT(1) DEFAULT '0' NOT NULL, disliked TINYINT(1) DEFAULT '0' NOT NULL, updated_at DATETIME NOT NULL, INDEX IDX_B66CB196537A1329 (message_id), INDEX IDX_B66CB196A76ED395 (user_id), INDEX idx_message_likes_uid_mid (message_id, user_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB;
ALTER TABLE message_likes ADD CONSTRAINT FK_B66CB196537A1329 FOREIGN KEY (message_id) REFERENCES message (id) ON DELETE CASCADE;
ALTER TABLE message_likes ADD CONSTRAINT FK_B66CB196A76ED395 FOREIGN KEY (user_id) REFERENCES user (id) ON DELETE CASCADE;
```

 - edit src/Chamilo/CoreBundle/Entity/MessageLikes.php
   and follow the instructions about the @Orm\Entity() line
 - edit src/Chamilo/CoreBundle/Entity/Message.php
   and fllow the instruccions about the @Orm\OneToMany line for the $likes property
 - launch composer install to rebuild the autoload.php
  • Loading branch information...
AngelFQC committed Mar 15, 2019
1 parent 2e92ad6 commit 9af667f5af4f90180ed69ebfb2aeda354ccd6198
@@ -1,6 +1,9 @@
<?php
/* For licensing terms, see /license.txt */
use Chamilo\CoreBundle\Component\Utils\ChamiloApi;
use Chamilo\CoreBundle\Entity\Message;
use Chamilo\CoreBundle\Entity\MessageLikes;
use ChamiloSession as Session;
/**
@@ -332,6 +335,101 @@
}
echo $html;
break;
case 'like_message':
header('Content-Type: application/json');
if (
api_is_anonymous() ||
!api_get_configuration_value('social_enable_likes_messages')
) {
echo json_encode(false);
exit;
}
$messageId = isset($_GET['id']) ? (int) $_GET['id'] : 0;
$status = isset($_GET['status']) ? $_GET['status'] : '';
$groupId = isset($_GET['group']) ? (int) $_GET['group'] : 0;
if (empty($messageId) || !in_array($status, ['like', 'dislike'])) {
echo json_encode(false);
exit;
}
$em = Database::getManager();
$messageRepo = $em->getRepository('ChamiloCoreBundle:Message');
$messageLikesRepo = $em->getRepository('ChamiloCoreBundle:MessageLikes');
/** @var Message $message */
$message = $messageRepo->find($messageId);
if (empty($message)) {
echo json_encode(false);
exit;
}
if ((int) $message->getGroupId() !== $groupId) {
echo json_encode(false);
exit;
}
if (!empty($message->getGroupId())) {
$usergroup = new UserGroup();
$groupInfo = $usergroup->get($groupId);
if (empty($groupInfo)) {
echo json_encode(false);
exit;
}
$isMember = $usergroup->is_group_member($groupId, $current_user_id);
if (GROUP_PERMISSION_CLOSED == $groupInfo['visibility'] && !$isMember) {
echo json_encode(false);
exit;
}
}
$user = api_get_user_entity($current_user_id);
$userLike = $messageLikesRepo->findOneBy(['message' => $message, 'user' => $user]);
if (empty($userLike)) {
$userLike = new MessageLikes();
$userLike
->setMessage($message)
->setUser($user);
}
if ('like' === $status) {
if ($userLike->isLiked()) {
echo json_encode(false);
exit;
}
$userLike
->setLiked(true)
->setDisliked(false);
} elseif ('dislike' === $status) {
if ($userLike->isDisliked()) {
echo json_encode(false);
exit;
}
$userLike
->setLiked(false)
->setDisliked(true);
}
$userLike
->setUpdatedAt(
api_get_utc_datetime(null, false, true)
);
$em->persist($userLike);
$em->flush();
echo json_encode(true);
break;
default:
echo '';
}
@@ -1691,6 +1691,10 @@ public static function display_message_for_group($groupId, $topic_id, $is_member
);
}
if (api_get_configuration_value('social_enable_likes_messages')) {
$links .= self::getLikesButton($main_message['id'], $current_user_id, $groupId);
}
$urlReply = $webCodePath.'social/message_for_group_form.inc.php?'
.http_build_query(
[
@@ -1827,6 +1831,11 @@ public static function display_message_for_group($groupId, $topic_id, $is_member
false
);
}
if (api_get_configuration_value('social_enable_likes_messages')) {
$links .= self::getLikesButton($topic['id'], $current_user_id, $groupId);
}
$links .= Display::toolbarButton(
$langReply,
$webCodePath.'social/message_for_group_form.inc.php?'
@@ -2750,4 +2759,79 @@ private static function getCountNewMessagesFromDB($userId)
return $row['count'];
}
/**
* @param int $messageId
*
* @return array
*
* @throws \Doctrine\ORM\Query\QueryException
*/
public static function countLikesAndDislikes($messageId, $userId)
{
$em = Database::getManager();
$likesCount = $em
->createQuery('SELECT COUNT(l) FROM ChamiloCoreBundle:MessageLikes l
WHERE l.liked = true AND l.message = :message')
->setParameters(['message' => (int) $messageId])
->getSingleScalarResult();
$dislikesCount = $em
->createQuery('SELECT COUNT(l) FROM ChamiloCoreBundle:MessageLikes l
WHERE l.liked = false AND l.message = :message')
->setParameters(['message' => (int) $messageId])
->getSingleScalarResult();
$userLike = $em
->getRepository('ChamiloCoreBundle:MessageLikes')
->findOneBy(['message' => $messageId, 'user' => $userId]);
return [
'likes' => $likesCount,
'dislikes' => $dislikesCount,
'user_liked' => $userLike ? $userLike->isLiked() : false,
'user_disliked' => $userLike ? $userLike->isDisliked() : false,
];
}
/**
* @param int $messageId
* @param int $userId
* @param int $groupId Optional.
*
* @return string
* @throws \Doctrine\ORM\Query\QueryException
*/
public static function getLikesButton($messageId, $userId, $groupId = 0)
{
$countLikes = self::countLikesAndDislikes($messageId, $userId);
$btnLike = Display::button(
'like',
Display::returnFontAwesomeIcon('thumbs-up', '', true)
.PHP_EOL.'<span>'.$countLikes['likes'].'</span>',
[
'title' => get_lang('Like'),
'class' => 'btn btn-default social-like '.($countLikes['user_liked'] ? 'disabled' : ''),
'data-status' => 'like',
'data-message' => $messageId,
'data-group' => $groupId,
]
);
$btnDislike = Display::button(
'like',
Display::returnFontAwesomeIcon('thumbs-down', '', true)
.PHP_EOL.'<span>'.$countLikes['dislikes'].'</span>',
[
'title' => get_lang('Dislike'),
'class' => 'btn btn-default social-like '.($countLikes['user_disliked'] ? 'disabled' : ''),
'data-status' => 'dislike',
'data-message' => $messageId,
'data-group' => $groupId,
]
);
return $btnLike.PHP_EOL.$btnDislike;
}
}
@@ -1801,7 +1801,7 @@ public static function getWallPostComments(
) {
$messageId = $messageInfo['id'];
$messages = MessageManager::getMessagesByParent($messageInfo['id'], 0, $offset, $limit);
$formattedList = '<div class="sub-mediapost">';
$formattedList = '<div class="sub-mediapost row">';
$users = [];
// The messages are ordered by date descendant, for comments we need ascendant
@@ -1857,16 +1857,16 @@ public static function processPostComment($message, $users = [])
$isAdmin = self::is_admin($users[$userIdLoop]['id']);
if ($userStatus === 5) {
if ($users[$userIdLoop]['has_certificates']) {
$iconStatus = '<img class="pull-left" src="'.$urlImg.'icons/svg/identifier_graduated.svg" width="22px" height="22px">';
$iconStatus = '<img src="'.$urlImg.'icons/svg/identifier_graduated.svg" width="22px" height="22px">';
} else {
$iconStatus = '<img class="pull-left" src="'.$urlImg.'icons/svg/identifier_student.svg" width="22px" height="22px">';
$iconStatus = '<img src="'.$urlImg.'icons/svg/identifier_student.svg" width="22px" height="22px">';
}
} else {
if ($userStatus === 1) {
if ($isAdmin) {
$iconStatus = '<img class="pull-left" src="'.$urlImg.'icons/svg/identifier_admin.svg" width="22px" height="22px">';
$iconStatus = '<img src="'.$urlImg.'icons/svg/identifier_admin.svg" width="22px" height="22px">';
} else {
$iconStatus = '<img class="pull-left" src="'.$urlImg.'icons/svg/identifier_teacher.svg" width="22px" height="22px">';
$iconStatus = '<img src="'.$urlImg.'icons/svg/identifier_teacher.svg" width="22px" height="22px">';
}
}
}
@@ -1884,7 +1884,7 @@ class="avatar-thumb">
</a>';
$comment .= '</div>';
$comment .= '</div>';
$comment .= '<div class="col-md-9 col-xs-9 social-post-answers">';
$comment .= '<div class="col-md-7 col-xs-7 social-post-answers">';
$comment .= '<div class="user-data">';
$comment .= $iconStatus;
$comment .= '<div class="username"><a href="'.$url.'">'.$nameComplete.'</a>
@@ -1895,23 +1895,31 @@ class="avatar-thumb">
$comment .= '</div>';
$comment .= '</div>';
$comment .= '<div class="col-md-3 col-xs-3 social-post-answers">';
$comment .= '<div class="pull-right btn-group btn-group-sm">';
$comment .= MessageManager::getLikesButton(
$message['id'],
$currentUserId
);
$isOwnWall = $currentUserId == $userIdLoop || $currentUserId == $receiverId;
if ($isOwnWall) {
$comment .= '<div class="col-md-1 col-xs-1 social-post-answers">';
$comment .= '<div class="pull-right deleted-mgs">';
$comment .= Display::url(
Display::returnFontAwesomeIcon('trash'),
Display::returnFontAwesomeIcon('trash', '', true),
'javascript:void(0)',
[
'id' => 'message_'.$message['id'],
'title' => get_lang('SocialMessageDelete'),
'onclick' => 'deleteComment('.$message['id'].')',
'class' => 'btn btn-default',
]
);
$comment .= '</div>';
$comment .= '</div>';
);
}
$comment .= '</div>';
$comment .= '</div>';
$comment .= '</div>';
return $comment;
}
@@ -3123,20 +3131,29 @@ private static function headerMessagePost($authorInfo, $receiverInfo, $message)
$html = '';
$html .= '<div class="top-mediapost" >';
$html .= '<div class="pull-right btn-group btn-group-sm">';
$html .= MessageManager::getLikesButton(
$message['id'],
$currentUserId,
!empty($message['group_info']['id']) ? (int) $message['group_info']['id'] : 0
);
if ($canEdit) {
$htmlDelete = Display::url(
Display::returnFontAwesomeIcon('trash'),
Display::returnFontAwesomeIcon('trash', '', true),
'javascript:void(0)',
[
'id' => 'message_'.$message['id'],
'title' => get_lang('SocialMessageDelete'),
'onclick' => 'deleteMessage('.$message['id'].')',
'class' => 'btn btn-default',
]
);
$html .= '<div class="pull-right deleted-mgs">';
$html .= $htmlDelete;
$html .= '</div>';
}
$html .= '</div>';
$html .= '<div class="user-image" >';
$html .= '<a href="'.$urlAuthor.'">
@@ -3151,7 +3168,7 @@ private static function headerMessagePost($authorInfo, $receiverInfo, $message)
$html .= '<div class="post-attachment" >';
$html .= $postAttachment;
$html .= '</div>';
$html .= '<p>'.Security::remove_XSS($message['content']).'</p>';
$html .= '<div>'.Security::remove_XSS($message['content']).'</div>';
$html .= '</div>';
$html .= '</div>'; // end mediaPost
@@ -1144,6 +1144,18 @@
// Avoid add a reply-to header when a no-reply address is set.
//$_configuration['mail_no_reply_avoid_reply_to'] = false;
// Allows to user add likes or dislikes to posts in social wall. Requires DB changes:
// CREATE TABLE message_likes (id BIGINT AUTO_INCREMENT NOT NULL, message_id BIGINT NOT NULL, user_id INT NOT NULL, liked TINYINT(1) DEFAULT '0' NOT NULL, disliked TINYINT(1) DEFAULT '0' NOT NULL, updated_at DATETIME NOT NULL, INDEX IDX_B66CB196537A1329 (message_id), INDEX IDX_B66CB196A76ED395 (user_id), INDEX idx_message_likes_uid_mid (message_id, user_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB;
// ALTER TABLE message_likes ADD CONSTRAINT FK_B66CB196537A1329 FOREIGN KEY (message_id) REFERENCES message (id) ON DELETE CASCADE;
// ALTER TABLE message_likes ADD CONSTRAINT FK_B66CB196A76ED395 FOREIGN KEY (user_id) REFERENCES user (id) ON DELETE CASCADE;
// In 1.11.8, before enabling this feature, you also need to:
// - edit src/Chamilo/CoreBundle/Entity/MessageLikes.php
// and follow the instructions about the @ORM\Entity() line
// - edit src/Chamilo/CoreBundle/Entity/Message.php
// and fllow the instruccions about the @ORM\OneToMany line for the $likes property
// - launch composer install to rebuild the autoload.php
//$_configuration['social_enable_likes_messages'] = false;
// KEEP THIS AT THE END
// -------- Custom DB changes
// Add user activation by confirmation email
@@ -207,6 +207,5 @@ function(responseText, textStatus, XMLHttpRequest) {
$tpl->assign('social_friend_block', $friend_html);
$tpl->assign('group_message', $group_message);
$tpl->assign('social_right_content', $social_right_content);
$tpl->assign('content', $content);
$social_layout = $tpl->get_template('social/groups_topics.tpl');
$tpl->display($social_layout);

5 comments on commit 9af667f

@jmontoyaa

This comment has been minimized.

Copy link
Member

replied Mar 18, 2019

Algunos comentarios:

  1. No es una buena idea de solo restringir esa tabla a "likes". Cambiar el nombre de la tabla a "MessageFeedback".

  2. La estructura de la tabla no me parece la mejor, según esta tabla un usuario puedo podría hacer like y dislike a la vez. Lo mejor sería de poner un campo "status" o "reaction" o "feedback" y usar una constante: int definida en la clase por ejemplo:

0 = no hay feedback
1 = like
2 = dislike.

Con esto se amplía las posibilidades de la tabla, pues también se podría usar para "reportar" mensajes negativos, si simplemente se agrega un status "flagged" o "reported".

La variable debería de ser:

$_configuration['social_enable_message_feedback']

@jmontoyaa

This comment has been minimized.

Copy link
Member

replied Mar 18, 2019

Estos cambios generan errores PHP, asi la opción no esté activada:

Fatal error: Uncaught Doctrine\ORM\Mapping\MappingException: Class "Chamilo\CoreBundle\Entity\MessageLikes"

@jmontoyaa

This comment has been minimized.

Copy link
Member

replied Mar 18, 2019

Corregido con:

9b25f66

@ywarnier

This comment has been minimized.

Copy link
Member

replied Mar 18, 2019

Hola @jmontoyaa,

Para el nombre de la tabla estoy de acuerdo, es más amplio y funciona bien con "feedback" en vez de "like".

Para su estructura, está pensada en optimización, ya que esta tabla podría tener un múltiple del tamaño de la tabla message (que ya puede ser muy grande), ya que permite a cada usuario votar cualquier mensaje. En este sentido, y dado que vamos a querer mostrar por separado los likes y los dislikes, el query resultará más eficiente si podemos hacer (en un solo query) un

SELECT SUM(liked), SUM(disliked) FROM message_feedback WHERE message_id = 184783;

De lo contrario tendríamos que hacer un query where message_id = 184783 AND feedback < 0, y luego otro query al revés.
Aquí se trata entonces de una optimización de lectura de la tabla (lo cual ocurrirá mucho más seguido que la escritura) para acelerar el funcionamiento de la red social una vez este mecanismo esté funcionando.

Está bien para el cambio de variable de configuración también.

@AngelFQC te dejo cambiar el nombre de la tabla, la entidad y la variable.

@jmontoyaa

This comment has been minimized.

Copy link
Member

replied Mar 19, 2019

Si esa fue la prioridad, entonces esta bien.

Please sign in to comment.
You can’t perform that action at this time.