gestion de réponse à msg de chat (ref #8)

This commit is contained in:
afornerot 2020-06-12 14:02:19 +02:00
parent b49c25e07e
commit a379e52dea
7 changed files with 311 additions and 16 deletions

View File

@ -19,7 +19,7 @@ if [ "$1" == "restartifdown" ] && [ -n "${pid}" ] ; then
restart="no" restart="no"
fi fi
if [[ "$restart" == "yes" ]]; then if [ "$restart" == "yes" ] && [ "$1" != "stop" ] ; then
echo WEBSOCKET = START echo WEBSOCKET = START
cd /var/www/html/ninegate cd /var/www/html/ninegate
bin/console gos:websocket:server --port $websocket_portinterne -a $websocket_url --no-debug -n -q --env=prod & disown bin/console gos:websocket:server --port $websocket_portinterne -a $websocket_url --no-debug -n -q --env=prod & disown

View File

@ -386,7 +386,7 @@
else { else {
var myclass=""; var myclass="";
if(forcereload) myclass="pageframereload"; if(forcereload) myclass="pageframereload";
$("#pagecontainer").append("<iframe id='frameitem-"+id+"' class='pageframe "+myclass+"' src='"+url+"' style='border:none; width:100%'></iframe>"); $("#pagecontainer").append("<iframe onload='this.contentWindow.focus()' id='frameitem-"+id+"' class='pageframe "+myclass+"' src='"+url+"' style='border:none; width:100%'></iframe>");
} }
resizeFrame(); resizeFrame();
@ -428,7 +428,7 @@
else { else {
var myclass=""; var myclass="";
if(forcereload) myclass="pageframereload"; if(forcereload) myclass="pageframereload";
$("#pagecontainer").append("<iframe id='page-"+id+"' data-category='"+catid+"' class='pageframe "+myclass+"' src='"+url+"' style='border:none; width:100%'></iframe>"); $("#pagecontainer").append("<iframe onload='this.contentWindow.focus()' id='page-"+id+"' data-category='"+catid+"' class='pageframe "+myclass+"' src='"+url+"' style='border:none; width:100%'></iframe>");
} }
// Détruire le badge associé car normalement de fait on a lu les notif // Détruire le badge associé car normalement de fait on a lu les notif
@ -436,7 +436,6 @@
$("#badge-"+groupid).remove() $("#badge-"+groupid).remove()
} }
// Cacher les actions possibles sur la page // Cacher les actions possibles sur la page
$("#menuupdate").hide(); $("#menuupdate").hide();
$("#menushare").hide(); $("#menushare").hide();
@ -459,11 +458,14 @@
// On resize les frame // On resize les frame
resizeFrame(); resizeFrame();
// Mettre le focus dans la frame
//$("#page-"+id).contentWindow.focus();
} }
// Affichages des pages // Affichages des pages
function showGoto(url) { function showGoto(url) {
$("#pagecontainer").append("<iframe id='goto' class='pageframe pageframereload' src='"+url+"' style='border:none; width:100%'></iframe>"); $("#pagecontainer").append("<iframe onload='this.contentWindow.focus()' id='goto' class='pageframe pageframereload' src='"+url+"' style='border:none; width:100%'></iframe>");
// On resize les frame // On resize les frame
resizeFrame(); resizeFrame();

View File

@ -22,7 +22,7 @@
{% endif %} {% endif %}
<div id="pageiframe" style="{% if entity.maxwidth>0%} max-width:{{ entity.maxwidth }}px; margin:auto; {% endif %}"> <div id="pageiframe" style="{% if entity.maxwidth>0%} max-width:{{ entity.maxwidth }}px; margin:auto; {% endif %}">
<iframe src="{{entity.url|replace({'#login#': username})}}" id="frameContent" style="border:none;" width="100%" height="100%"></iframe> <iframe onload='this.contentWindow.focus()' src="{{entity.url|replace({'#login#': username})}}" id="frameContent" style="border:none;" width="100%" height="100%"></iframe>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -55,8 +55,8 @@ class ChatController extends Controller
} }
// Récupération des message du groupe // Récupération des message parent du groupe
$messages=$em->getRepository("CadolesWebsocketBundle:Message")->findBy(["group"=>$group],["submitdate"=>"DESC"],30); $messages=$em->getRepository("CadolesWebsocketBundle:Message")->findBy(["group"=>$group,"parent"=>null],["submitdate"=>"DESC"],30);
foreach($messages as $message) { foreach($messages as $message) {
$haveread = ($message->getReaders()->contains($user)); $haveread = ($message->getReaders()->contains($user));
$havesee = ($message->getSees()->contains($user)); $havesee = ($message->getSees()->contains($user));
@ -69,6 +69,20 @@ class ChatController extends Controller
$em->persist($message); $em->persist($message);
$em->flush(); $em->flush();
} }
foreach($message->getChilds() as $child) {
$haveread = ($child->getReaders()->contains($user));
$havesee = ($child->getSees()->contains($user));
$child->setHaveread($haveread);
$child->setHavesee($havesee);
if(!$haveread) {
$child->addReader($this->getUser());
$em->persist($child);
$em->flush();
}
}
} }
// Création du formulaire // Création du formulaire
@ -141,8 +155,8 @@ class ChatController extends Controller
$usergroup=$em->getRepository("CadolesCoreBundle:UserGroup")->findOneBy(["group"=>$group,"user"=>$user]); $usergroup=$em->getRepository("CadolesCoreBundle:UserGroup")->findOneBy(["group"=>$group,"user"=>$user]);
if(!$usergroup) die(); if(!$usergroup) die();
// On récupere les messages // On récupere les messages parent
$messages=$em->getRepository("CadolesWebsocketBundle:Message")->findBy(["group"=>$group],["submitdate"=>"DESC"],10); $messages=$em->getRepository("CadolesWebsocketBundle:Message")->findBy(["group"=>$group,"parent"=>null],["submitdate"=>"DESC"],10);
foreach($messages as $message) { foreach($messages as $message) {
$tmp=[]; $tmp=[];
@ -150,9 +164,21 @@ class ChatController extends Controller
$tmp["message"]=$message->getTopic(); $tmp["message"]=$message->getTopic();
$tmp["submitdate"]=$message->getSubmitdate(); $tmp["submitdate"]=$message->getSubmitdate();
$tmp["userid"]=$message->getUser()->getId(); $tmp["userid"]=$message->getUser()->getId();
$tmp["userlastname"]=$message->getUser()->getLastname(); $tmp["userlastname"]=$message->getUser()->getLastname()." ".$message->getUser()->getFirstname();
$tmp["userfirstname"]=$message->getUser()->getFirstname();
$tmp["useravatar"]=$message->getUser()->getAvatar(); $tmp["useravatar"]=$message->getUser()->getAvatar();
$tmp["childs"]=[];
foreach($message->getChilds() as $child) {
$tmpchild=[];
$tmpchild["id"]=$child->getId();
$tmpchild["message"]=$child->getTopic();
$tmpchild["submitdate"]=$child->getSubmitdate();
$tmpchild["userid"]=$child->getUser()->getId();
$tmpchild["userlastname"]=$child->getUser()->getLastname()." ".$child->getUser()->getFirstname();
$tmpchild["useravatar"]=$child->getUser()->getAvatar();
array_push($tmp["childs"],$tmpchild);
}
array_push($output,$tmp); array_push($output,$tmp);
} }

View File

@ -40,6 +40,16 @@ class Message
*/ */
private $group; private $group;
/**
* @ORM\ManyToOne(targetEntity="Cadoles\WebsocketBundle\Entity\message", inversedBy="childs")
*/
private $parent;
/**
* @ORM\OneToMany(targetEntity="Cadoles\WebsocketBundle\Entity\Message", mappedBy="parent", cascade={"persist"}, orphanRemoval=true)
*/
protected $childs;
/** /**
* @ORM\ManyToMany(targetEntity="Cadoles\CoreBundle\Entity\User", inversedBy="messagereaders", cascade={"persist"}) * @ORM\ManyToMany(targetEntity="Cadoles\CoreBundle\Entity\User", inversedBy="messagereaders", cascade={"persist"})
* @ORM\JoinTable(name="messageuserread", * @ORM\JoinTable(name="messageuserread",
@ -265,4 +275,62 @@ class Message
{ {
return $this->sees; return $this->sees;
} }
/**
* Set parent
*
* @param \Cadoles\WebsocketBundle\Entity\message $parent
*
* @return Message
*/
public function setParent(\Cadoles\WebsocketBundle\Entity\message $parent = null)
{
$this->parent = $parent;
return $this;
}
/**
* Get parent
*
* @return \Cadoles\WebsocketBundle\Entity\message
*/
public function getParent()
{
return $this->parent;
}
/**
* Add child
*
* @param \Cadoles\WebsocketBundle\Entity\Message $child
*
* @return Message
*/
public function addChild(\Cadoles\WebsocketBundle\Entity\Message $child)
{
$this->childs[] = $child;
return $this;
}
/**
* Remove child
*
* @param \Cadoles\WebsocketBundle\Entity\Message $child
*/
public function removeChild(\Cadoles\WebsocketBundle\Entity\Message $child)
{
$this->childs->removeElement($child);
}
/**
* Get childs
*
* @return \Doctrine\Common\Collections\Collection
*/
public function getChilds()
{
return $this->childs;
}
} }

View File

@ -46,6 +46,33 @@
line-height: 12px; line-height: 12px;
margin-bottom: 10px; margin-bottom: 10px;
} }
.msgreplys {
margin-bottom:10px;
}
.messagereply {
padding:0px;
border-top: 1px solid #{{ colorbodyfont }};
font-size:12px;
}
.btnreplymessage {
padding: 5px;
background-color: #{{ colorbodyfont }};
color : #{{ colorbodyback }};
border-radius:5px;
cursor:pointer;
}
.btnreplymessage:hover {
color : #{{ colorbodyback }};
}
.replymessage { display:none}
.replymessage textarea {
width:100%;
margin:20px 0px 5px 0px;
height:150px;
}
.sendreply { width:100%}
.bootstrap-switch{ .bootstrap-switch{
margin-top:5px; margin-top:5px;
} }
@ -104,6 +131,39 @@
<small>{{message.submitdate|date('d/m/Y H:i')}}</small> <small>{{message.submitdate|date('d/m/Y H:i')}}</small>
</div> </div>
<div class='msgtopic'>{{message.topic | raw}}</div> <div class='msgtopic'>{{message.topic | raw}}</div>
<div id="msgreplys-{{message.id}}" class='msgreplys'>
{% for child in message.childs | reverse %}
{% set classread="" %}
{% if not child.haveread %}
{% set classread="message-toread" %}
{% endif %}
{% set classsee="" %}
{% if child.havesee %}
{% set classsee="message-see" %}
{% endif %}
<div id='message-{{child.id}}' class='message messagereply {{classread}} {{classsee}}'>
{{child.topic | raw | nl2br }}
<div style='cursor:pointer' onclick='seeUser({{child.user.id}})'><small>{{ child.user.lastname }} {{ child.user.firstname }}</small></div>
<small>{{child.submitdate|date('d/m/Y H:i')}}</small>
{% if fgmanager or child.user == app.user %}
<i class='delmessage fa fa-trash fa-fw' data-id='{{child.id}}' title='Supprimer' style='cursor: pointer;'></i>
{% endif %}
{% if not child.havesee %}
<i id="hidemessage-{{child.id}}" class='hidemessage fa fa-eye-slash fa-fw' title='Ne plus afficher' data-id='{{child.id}}' style='cursor: pointer;'></i>
{% endif %}
</div>
{% endfor %}
</div>
<a class="btnreplymessage" data-id='{{message.id}}'>Répondre</a>
<div id="replymessage-{{message.id}}" class="replymessage">
<textarea></textarea>
<a id="sendreply" class="btn btn-success sendreply" data-id='{{message.id}}'>Envoyer</a>
</div>
</div> </div>
</div> </div>
{% endfor %} {% endfor %}
@ -316,6 +376,12 @@
html+="<div class='msgdiv'>" html+="<div class='msgdiv'>"
html+="<div class='msgtitle'>"+payload.msg.lastname+"<br><small>"+new Date(payload.msg.submitdate.date).toLocaleDateString("fr-FR", dateoptions)+"</small></div>"; html+="<div class='msgtitle'>"+payload.msg.lastname+"<br><small>"+new Date(payload.msg.submitdate.date).toLocaleDateString("fr-FR", dateoptions)+"</small></div>";
html+="<div class='msgtopic'>"+payload.msg.message+"</div>"; html+="<div class='msgtopic'>"+payload.msg.message+"</div>";
html+="<div id='msgreplys-"+payload.msg.id+"' class='msgreplys'></div>";
html+="<a class='btnreplymessage' data-id='"+payload.msg.id+"'>Répondre</a>";
html+="<div id='replymessage-"+payload.msg.id+"' class='replymessage'>";
html+="<textarea></textarea>";
html+="<a id='sendreply' class='btn btn-success sendreply' data-id='"+payload.msg.id+"'>Envoyer</a>";
html+="</div>";
html+="</div>"; html+="</div>";
html+="</div>"; html+="</div>";
$(".mychat").prepend(html); $(".mychat").prepend(html);
@ -323,6 +389,24 @@
//console.log("Received message by "+payload.msg.userid+" = ",payload.msg); //console.log("Received message by "+payload.msg.userid+" = ",payload.msg);
} }
if(payload.msgreply) {
// Si on reçoit une réponse forcement le parent est de nouveau visible
$("#message-"+payload.msgreply.parent).removeClass ("message-see");
// On ajout la réponse
html ="<div id='message-"+payload.msgreply.id+"' class='message messagereply message-toread'>";
html+=nl2br(payload.msgreply.message);
html+="<div style='cursor:pointer' onclick='seeUser("+payload.msgreply.userid+")'><small>"+payload.msgreply.lastname+"</small></div>";
html+="<small>"+new Date(payload.msgreply.submitdate.date).toLocaleDateString("fr-FR", dateoptions)+"</small>";
if(payload.msgreply.userid=={{app.user.id}} || '{{ fgmanager }}'=='1') {
html+="<i class='delmessage fa fa-trash fa-fw' data-id='"+payload.msgreply.id+"' title='Supprimer' style='cursor: pointer;'></i>";
}
html+="<i id='hidemessage-"+payload.msgreply.id+"' class='hidemessage fa fa-eye-slash fa-fw' data-id='"+payload.msgreply.id+"' title='Ne plus afficher' style='cursor: pointer;'></i>";
html+="</div>";
$("#msgreplys-"+payload.msgreply.parent).prepend(html);
}
if(payload.log) { if(payload.log) {
console.log("Received message", payload.log); console.log("Received message", payload.log);
} }
@ -367,6 +451,14 @@
}); });
} }
function nl2br (str, is_xhtml) {
if (typeof str === 'undefined' || str === null) {
return '';
}
var breakTag = (is_xhtml || typeof is_xhtml === 'undefined') ? '<br />' : '<br>';
return (str + '').replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1' + breakTag + '$2');
}
function switchsee() { function switchsee() {
$(".message-see").hide(); $(".message-see").hide();
if($("#unsee").bootstrapSwitch('state')) { if($("#unsee").bootstrapSwitch('state')) {
@ -515,8 +607,52 @@
} }
} }
}); });
}
$(document).on('click', '.btnreplymessage', function(){
btnreplymessage($(this).data("id"));
});
function btnreplymessage(id) {
$("#replymessage-"+id).toggle();
$("#replymessage-"+id+" textarea").focus();
}
$(document).on('click', '.sendreply', function(){
sendreply($(this).data("id"));
});
function sendreply(id) {
try {
// Flager que l'envoit c'est fait
tosend=false;
$("#replymessage-"+id+" a").html("Envoyer");
$("#replymessage-"+id+" a").removeClass("btn-danger");
$("#replymessage-"+id+" a").css('cursor','auto');
// Push envent sur le websocket
var data = "<p>"+$("#replymessage-"+id+" textarea").val()+"</p>";
event={mykey: "{{userkey}}", type: "reply", message: data, parent: id};
session.publish("websocket/channel/{{groupid}}", event);
$("#replymessage-"+id+" textarea").val("");
$("#replymessage-"+id).hide();
}
catch(error) {
// L'envoi ne s'est pas fait
console.log("Retry to send");
// On flag le fait de retenter l'envoit à la reconnexion
tosend=true;
// On signal que l'envoi est en cours sur le bouton
$("#replymessage-"+id+" a").html("Envoi en cours");
$("#replymessage-"+id+" a").addClass("btn-danger");
$("#replymessage-"+id+" a").css('cursor','progress');
}
} }
$(document).on('click', '#listtoavatar img', function(){ $(document).on('click', '#listtoavatar img', function(){
var mail = ";"+$(this).data("mail"); var mail = ";"+$(this).data("mail");
$("#mailto").val($("#mailto").val().replace(mail, "")); $("#mailto").val($("#mailto").val().replace(mail, ""));
@ -524,6 +660,8 @@
}); });
function recuplastmsg() { function recuplastmsg() {
console.log("recuplastmsg");
$.ajax({ $.ajax({
method: "POST", method: "POST",
url: "{{ path('cadoles_websocket_message_list') }}", url: "{{ path('cadoles_websocket_message_list') }}",
@ -534,20 +672,47 @@
$.each(datas, function(i,data) { $.each(datas, function(i,data) {
// Le message a-t-il été déposé durant la déconnexion ? // Le message a-t-il été déposé durant la déconnexion ?
if(!$("#message-"+data.id).length) { if(!$("#message-"+data.id).length) {
html ="<div id='message-"+data.id+"' class='message row'>"; html ="<div id='message-"+data.id+"' class='message row message-toread'>";
html+="<div class='msgavatar'>"; html+="<div class='msgavatar'>";
html+="<img style='cursor:pointer' onclick='seeUser("+data.userid+")' id='user_avatar_img' src='/{{ alias }}/uploads/avatar/"+data.useravatar+"' class='avatar'><br>"; html+="<img style='cursor:pointer' onclick='seeUser("+data.userid+")' id='user_avatar_img' src='/{{ alias }}/uploads/avatar/"+data.useravatar+"' class='avatar'><br>";
if(data.userid=={{app.user.id}} || '{{ fgmanager }}'=='1') { if(data.userid=={{app.user.id}} || '{{ fgmanager }}'=='1') {
html+="<i class='delmessage fa fa-trash fa-fw' data-id='"+data.id+"' style='cursor: pointer;'></i>"; html+="<i class='delmessage fa fa-trash fa-fw' data-id='"+data.id+"' style='cursor: pointer;'></i>";
} }
if (!data.havesee) {
html+="<i id='hidemessage-"+data.id+"' class='hidemessage fa fa-eye-slash fa-fw' data-id='"+data.id+"' title='Ne plus afficher' style='cursor: pointer;'></i>";
}
html+="</div>"; html+="</div>";
html+="<div class='msgdiv'>" html+="<div class='msgdiv'>"
html+="<div class='msgtitle'>"+data.userlastname+"<br><small>"+new Date(data.submitdate.date).toLocaleDateString("fr-FR", dateoptions)+"</small></div>"; html+="<div class='msgtitle'>"+data.userlastname+"<br><small>"+new Date(data.submitdate.date).toLocaleDateString("fr-FR", dateoptions)+"</small></div>";
html+="<div class='msgtopic'>"+data.message+"</div>"; html+="<div class='msgtopic'>"+data.message+"</div>";
html+="<div id='msgreplys-"+data.id+"' class='msgreplys'>";
html+="</div>";
html+="<a class='btnreplymessage' data-id='"+data.id+"'>Répondre</a>";
html+="<div id='replymessage-"+data.id+"' class='replymessage'>";
html+="<textarea></textarea>";
html+="<a id='sendreply' class='btn btn-success sendreply' data-id='"+data.id+"'>Envoyer</a>";
html+="</div>";
html+="</div>"; html+="</div>";
html+="</div>"; html+="</div>";
$(".mychat").prepend(html); $(".mychat").prepend(html);
} }
$.each(data.childs, function(i,child) {
if(!$("#message-"+child.id).length) {
html ="<div id='message-"+child.id+"' class='message messagereply message-toread'>";
html+=nl2br(child.message);
html+="<div style='cursor:pointer' onclick='seeUser("+child.userid+")'><small>"+child.userlastname+"</small></div>";
html+="<small>"+new Date(child.submitdate.date).toLocaleDateString("fr-FR", dateoptions)+"</small>";
if(child.userid=={{app.user.id}} || '{{ fgmanager }}'=='1') {
html+="<i class='delmessage fa fa-trash fa-fw' data-id='"+child.id+"' title='Supprimer' style='cursor: pointer;'></i>";
}
html+="<i id='hidemessage-"+child.id+"' class='hidemessage fa fa-eye-slash fa-fw' data-id='"+child.id+"' title='Ne plus afficher' style='cursor: pointer;'></i>";
html+="</div>";
$("#msgreplys-"+data.id).prepend(html);
}
});
}); });
} }
}); });

View File

@ -151,6 +151,40 @@ class WebsocketTopic implements TopicInterface
$topic->broadcast(['msg' => $return]); $topic->broadcast(['msg' => $return]);
} }
if($event["type"]=="reply") {
$parent=$this->em->getRepository("CadolesWebsocketBundle:Message")->find($event["parent"]);
if($parent) {
$message=new Message();
$message->setTopic($event["message"]);
$message->setUser($user);
$message->setGroup($group);
$message->setParent($parent);
$this->em->persist($message);
$this->em->flush();
// Si commentaire sur une message : il faut replacer le message en unsee pour tt le monde
foreach($parent->getSees() as $see) {
$parent->removeSee($see);
}
$this->em->persist($parent);
$this->em->flush();
$return["id"]=$message->getId();
$return["lastname"]=$user->getLastname()." ".$user->getFirstname();
$return["avatar"]=$user->getAvatar();
$return["submitdate"]=$message->getSubmitdate();
$return["message"]=$event["message"];
$return["userid"]=$user->getId();
$return["parent"]=$event["parent"];
//this will broadcast the message to ALL subscribers of this topic.
$topic->broadcast(['msgreply' => $return]);
}
else
$topic->broadcast(['log' => "Message parent introuvable"]);
}
if($event["type"]=="del") { if($event["type"]=="del") {
$message=$this->em->getRepository("CadolesWebsocketBundle:Message")->find($event["id"]); $message=$this->em->getRepository("CadolesWebsocketBundle:Message")->find($event["id"]);
if($message&&($usergroup->getFgmanager()||$message->getUser()==$user||$user->getRole()=="ROLE_ADMIN"||$user->getRole()=="ROLE_MODO" )) { if($message&&($usergroup->getFgmanager()||$message->getUser()==$user||$user->getRole()=="ROLE_ADMIN"||$user->getRole()=="ROLE_MODO" )) {