Compare commits

...

6 Commits

11 changed files with 296 additions and 32 deletions

View File

@ -1,5 +1,6 @@
/.web-server-pid /.web-server-pid
/app/config/parameters.yml /app/config/parameters.yml
/src/Cadoles/CoreBundle/Command/data/core-init-01.sql
/build/ /build/
/phpunit.xml /phpunit.xml
/var/* /var/*

View File

@ -8,7 +8,7 @@ INSERT IGNORE INTO `niveau01` (`id`, `label`, `siren`) VALUES
(-100, 'DRAAF', '130007107'); (-100, 'DRAAF', '130007107');
INSERT IGNORE INTO `user` (`id`, `niveau01_id`, `username`, `firstname`, `lastname`, `password`, `email`, `avatar`, `role`,`siren`,`authlevel`) VALUES INSERT IGNORE INTO `user` (`id`, `niveau01_id`, `username`, `firstname`, `lastname`, `password`, `email`, `avatar`, `role`,`siren`,`authlevel`) VALUES
(-100, -100, 'admin', 'Administrateur', 'draaf', '{SSHA}wVosXEVYfrthFQKc0AqqOtZZXDWT3re1 (-100, -100, 'admin', 'Administrateur', 'draaf', '{SSHA}e8qvl9iAOSAmoVpq/NkxAa4QSpRdn+Z2
', 'admin@ldapbundle.ac-arno.fr', 'admin.jpg', 'ROLE_ADMIN', '130007107', 'simple'); ', 'admin@ldapbundle.ac-arno.fr', 'admin.jpg', 'ROLE_ADMIN', '130007107', 'simple');

View File

@ -1,12 +1,46 @@
var templatetour;
templatetour ="<div class='popover tour' style='max-width:500px !important;'>";
templatetour+="<div class='arrow'></div>";
templatetour+="<h3 class='popover-title'></h3>";
templatetour+="<div class='popover-content'></div>";
templatetour+="<div class='popover-navigation'>";
templatetour+="<button class='btn btn-sm btn-default' data-role='prev'>« Prec</button>";
templatetour+="<button class='btn btn-sm btn-default' data-role='next'>Suiv »</button>";
templatetour+="<button class='btn btn-sm btn-default' data-role='end'>Fin</button>";
templatetour+="</div>";
templatetour+="<div class='popover-navigation'>"
templatetour+="<button onClick='$(\".popover\").hide(); top.callTour(\"MyTour\");' class='btn btn-sm btn-default'>Entête</button>";
templatetour+="<button onClick='$(\".popover\").hide(); top.callTour(\"MyTourAcceuil\");' class='btn btn-sm btn-default'>Page Accueil</button>";
templatetour+="</div>";
templatetour+="</div>";
function callTour(mytour) {
// Tour entête
if(mytour=="MyTour") {
// On lance le tour
top.MyTour();
}
// Tour Accueil
if(mytour=="MyTourAcceuil") {
// On force l'affichage de la premiere frame
top.$(".navbar-left").find("a").eq(0).click();
// On lance le tour au bout de 1s le temps que l'iframe se réalise
setTimeout(function(){top.$("iframe").first().prop('contentWindow').MyTourAccueil(); }, 2000);
}
}
// Instance the tour // Instance the tour
function MyTour() { function MyTour() {
var template="<div class='popover tour' style='max-width:500px !important;'><div class='arrow'></div><h3 class='popover-title'></h3><div class='popover-content'></div><div class='popover-navigation'><button class='btn btn-sm btn-default' data-role='prev'>« Prec</button><button class='btn btn-sm btn-default' data-role='next'>Suiv »</button><button class='btn btn-sm btn-default' data-role='end'>Fin</button></div></div>";
var tour = new Tour({ var tour = new Tour({
name: "tour", name: "tour",
template: template, template: templatetour,
onEnd: function (tour) {
$("#page-51").prop('contentWindow').MyTourAccueil();
},
steps: [ steps: [
{ {
element: "#logo", element: "#logo",
@ -41,56 +75,71 @@ function MyTour() {
element: "#appmenu", element: "#appmenu",
title: "Barre de navigation rapide", title: "Barre de navigation rapide",
placement : "bottom", placement : "bottom",
content: "Cette barre vous permet d'accéder rapidement à la page d'accueil et à vos groupes de travail" content: "Cette barre vous permet d'accéder rapidement à la page d'accueil et à vos groupes de travail",
}, },
]
});
// Initialize the tour
tour.init();
// Start the tour
tour.goTo(0);
tour.restart();
}
function MyTourAccueil() {
var tour = new Tour({
name: "touraccueil",
template: templatetour,
steps: [
{ {
element: $("iframe").first().contents().find(".widget-itemessential").find(".widgetheader"), element: $(".widget-itemessential").find(".widgetheader"),
title: "le Bureau", title: "le Bureau",
placement : "right", placement : "right",
content: "Dans le premier panneau d'outils ci-dessous, vous trouvez les applications qui vous sont proposées. En cliquant sur le bouton '+', vous avez accès à d'autres applications", content: "Dans le premier panneau d'outils ci-dessous, vous trouvez les applications qui vous sont proposées. En cliquant sur le bouton '+', vous avez accès à d'autres applications",
container: $("iframe").first().contents().find("body")
}, },
{ {
element: $("iframe").first().contents().find(".widget-group").find(".widgetheader"), element: $(".widget-group").find(".widgetheader"),
title: "Mes Groupes de travail", title: "Mes Groupes de travail",
placement : "right", placement : "right",
content: "Dans le 2nd panneau d'outils ci-dessous, vous trouvez les 'groupes de travail' auxquels vous avez été invités et ceux que vous avez créés. En cliquant sur le bouton '+', vous pouvez créer un groupe de travail. ", content: "Dans le 2nd panneau d'outils ci-dessous, vous trouvez les 'groupes de travail' auxquels vous avez été invités et ceux que vous avez créés. En cliquant sur le bouton '+', vous pouvez créer un groupe de travail. ",
container: $("iframe").first().contents().find("body")
}, },
{ {
element: $("iframe").first().contents().find(".widget-blog").find(".widgetheader"), element: $(".widget-group").find(".widgetheader"),
title: "Mes Groupes de travail",
placement : "right",
content: "Dans le 2nd panneau d'outils ci-dessous, vous trouvez les 'groupes de travail' auxquels vous avez été invités et ceux que vous avez créés. En cliquant sur le bouton '+', vous pouvez créer un groupe de travail. ",
},
{
element: $(".widget-blog").find(".widgetheader"),
title: "Journaux des groupes", title: "Journaux des groupes",
placement : "right", placement : "right",
content: "Dans le 3ème panneau d'outils ci-dessous, vous trouvez les 'Journaux des Groupes' auxquels vous avez été invités et ceux que vous avez créés. ", content: "Dans le 3ème panneau d'outils ci-dessous, vous trouvez les 'Journaux des Groupes' auxquels vous avez été invités et ceux que vous avez créés. ",
container: $("iframe").first().contents().find("body")
}, },
{ {
element: $("iframe").first().contents().find(".widget-alert").find(".widgetheader"), element: $(".widget-alert").find(".widgetheader"),
title: "Radio Transnum", title: "Radio Transnum",
placement : "left", placement : "left",
content: "Dans le premier panneau de droite ci-dessous, vous trouvez les annonces et nouvelles qui concernent tous les membres du portail. ", content: "Dans le premier panneau de droite ci-dessous, vous trouvez les annonces et nouvelles qui concernent tous les membres du portail. ",
container: $("iframe").first().contents().find("body")
}, },
{ {
element: $("iframe").first().contents().find(".widget-groupmessage").find(".widgetheader"), element: $(".widget-groupmessage").find(".widgetheader"),
title: "Actualités des groupes", title: "Actualités des groupes",
placement : "left", placement : "left",
content: "Dans le 2nd panneau de droite ci-dessous, vous trouvez les annonces et nouvelles qui vous concernent uniquement comme membre de groupes. ", content: "Dans le 2nd panneau de droite ci-dessous, vous trouvez les annonces et nouvelles qui vous concernent uniquement comme membre de groupes. ",
container: $("iframe").first().contents().find("body")
}, },
{ {
element: $("iframe").first().contents().find(".widget-calendar").find(".widgetheader"), element: $(".widget-calendar").find(".widgetheader"),
title: "Agenda des groupes", title: "Agenda des groupes",
placement : "left", placement : "left",
content: "Dans le troisième panneau de droite ci-dessous, vous trouvez les prochains évènements de vos agendas de groupes. ", content: "Dans le troisième panneau de droite ci-dessous, vous trouvez les prochains évènements de vos agendas de groupes. ",
container: $("iframe").first().contents().find("body")
}, },
{ {
element: $("iframe").first().contents().find(".widget-appexternal").find(".widgetheader"), element: $(".widget-appexternal").find(".widgetheader"),
placement : "left", placement : "left",
title: "Formulaires", title: "Formulaires",
content: "Dans le dernier panneau de droite ci-dessous, vous trouvez les formulaires qui sont mis à votre disposition pour effectuer des demandes (par exemple faire un 'ticket de support'). ", content: "Dans le dernier panneau de droite ci-dessous, vous trouvez les formulaires qui sont mis à votre disposition pour effectuer des demandes (par exemple faire un 'ticket de support'). ",
container: $("iframe").first().contents().find("body")
} }
] ]
}); });
@ -106,5 +155,6 @@ function MyTour() {
// Create Button Tour // Create Button Tour
$(document).on('ready', function(){ $(document).on('ready', function(){
// Instance the Tour // Instance the Tour
$("<li><a title='Tuto' style='cursor:pointer'><i onClick='MyTour()' class='fa fa-question fa-fw'></i></a></li>").insertBefore($("#menu-annuaire")); if($("#menu-annuaire".length))
$("<li><a title='Tuto' style='cursor:pointer'><i onClick='MyTour()' class='fa fa-question fa-fw'></i></a></li>").insertBefore($("#menu-annuaire"));
}); });

View File

@ -83,12 +83,12 @@
{% endjavascripts %} {% endjavascripts %}
{% endblock %} {% endblock %}
{% if useheader %}
{% set theme = app.session.get('theme') %} {% set theme = app.session.get('theme') %}
{% if theme is not empty %} {% if theme is not empty %}
<script src="/{{ alias }}/bundles/cadolescore/themes/{{theme}}/local.js"></script> <script src="/{{ alias }}/bundles/cadolescore/themes/{{theme}}/local.js"></script>
{% endif %} {% endif %}
{% endif %}
<script> <script>
function seeUser(id) { function seeUser(id) {

View File

@ -178,6 +178,30 @@ class InitDataCommand extends ContainerAwareCommand
elseif($entity&&!$activate_widmoodle) { elseif($entity&&!$activate_widmoodle) {
$this->entityManager->remove($entity); $this->entityManager->remove($entity);
} }
// Job notification message non lus
// Toute les 24h à 5h00
$websocket_activate = $this->getContainer()->getParameter('websocket_activate');
$entity = $this->entityManager->getRepository('CadolesCronBundle:Cron')->find(2000);
if(!$entity&&($portal_activate||$calendar_activate)) {
$entity = new Cron;
$nextdate=$entity->getSubmitdate();
$nextdate->setTime(5,0);
$entity->setCommand("Websocket:CountMessage");
$entity->setDescription("Notifier les utilisateurs des messages non lus");
$entity->setId(2000);
$entity->setStatut(2);
$entity->setRepeatcall(0);
$entity->setRepeatexec(0);
$entity->setRepeatinterval(86400);
$entity->setNextexecdate($nextdate);
$this->entityManager->persist($entity);
}
elseif($entity&&!($websocket_activate)) {
$this->entityManager->remove($entity);
}
// On flush
$this->entityManager->flush(); $this->entityManager->flush();
} }
} }

View File

@ -478,7 +478,7 @@ class PageController extends Controller
} }
// On marque tt les messages comme lu par l'utilisateur // On marque tt les messages comme lu par l'utilisateur
/* Se fait à présent sur le controleur message au moment meme l'on affiche l'ensemble des messages
if($usage=="group") { if($usage=="group") {
// On calcule le nombre de message non lu pour le groupe // On calcule le nombre de message non lu pour le groupe
$group=$em->getRepository("CadolesCoreBundle:Group")->find($groupid); $group=$em->getRepository("CadolesCoreBundle:Group")->find($groupid);
@ -513,6 +513,7 @@ class PageController extends Controller
} }
} }
} }
*/
// Type Calendrier // Type Calendrier

View File

@ -0,0 +1,107 @@
<?php
namespace Cadoles\WebsocketBundle\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Finder\Finder;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\HttpKernel\KernelInterface;
use Doctrine\DBAL\Connection as DBALConnection;
use Doctrine\ORM\EntityManager;
use Symfony\Component\Validator\Constraints\DateTime;
class CountMessageCommand extends Command
{ protected function configure()
{
$this
->setName('Websocket:CountMessage')
->setDescription('Count number of messages not read and send mail notification')
->setHelp('Count number of message')
->addArgument('cronid', InputArgument::OPTIONAL, 'ID Cron Job')
->addArgument('lastchance', InputArgument::OPTIONAL, 'Lastchance to run the cron')
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$this->container = $this->getApplication()->getKernel()->getContainer();
$this->em = $this->container->get('doctrine')->getEntityManager();
$this->output = $output;
$this->filesystem = new Filesystem();
$this->rootlog = $this->container->get('kernel')->getRootDir()."/../var/logs/";
$this->noreply = $this->container->getParameter('noreply');
$this->writelnred('');
$this->writelnred('== Websocket:Countmessage');
$this->writelnred('==========================================================================================================');
$now=new \DateTime('now');
$fgdebug = false;
// Config
$this->appname = $this->em->getRepository("CadolesCoreBundle:Config")->findOneBy(["id"=>"appname"])->getValue();
$this->url= "https://".$this->container->getParameter('weburl')."/".$this->container->getParameter('alias');
// Pour chaque utilisateur
$users=$this->em->getRepository("CadolesCoreBundle:User")->findAll();
foreach($users as $user) {
$cptnotread=0;
// Pour chaque group de l'utilisateur
foreach($user->getGroups() as $usergroup) {
// On calcule le nombre de message non lu pour l'utilisateur'
$group=$usergroup->getGroup();
$qb = $this->em->createQueryBuilder();
$tm = $qb ->select($qb->expr()->count('m.id'))
->from('CadolesWebsocketBundle:Message', 'm')
->where('m.group = :group')
->andWhere('m.user != :user')
->setParameter('group', $group)
->setParameter('user', $user)
->getQuery()->getSingleScalarResult();
$qb = $this->em->createQueryBuilder();
$tr = $qb ->select($qb->expr()->count('m.id'))
->from('CadolesWebsocketBundle:Message', 'm')
->where('m.group = :group')
->andWhere('m.user != :user')
->andWhere(':user MEMBER OF m.readers')
->setParameter('group', $group)
->setParameter('user', $user)
->getQuery()->getSingleScalarResult();
if($tm-$tr>0) $cptnotread+=($tm-$tr);
}
if($cptnotread>0) {
$this->writeln($user->getUsername()." notifié de ".$cptnotread." messages non lus");
$template="template";
$mail_params=array(
"subject" => $this->appname." : Messages non lus",
"body_html"=>"<p>Vous avez ".$cptnotread." messages non lus sur ".$this->appname."</p><p>Vous pouvez les consulter sur <a href='".$this->url."'>".$this->url."</a></p>",
"body_text"=>"Vous avez ".$cptnotread." messages non lus sur ".$this->appname."\nVous pouvez les consulter sur ".$this->url,
);
$message = $this->container->get('cadoles.core.service.mail');
$message->sendEmail($template, $mail_params, $user->getEmail(), $this->noreply, $this->appname);
}
}
$this->writeln('');
return 1;
}
private function writelnred($string) {
$this->output->writeln('<fg=red>'.$string.'</>');
$this->filesystem->appendToFile($this->rootlog.'cron.log', $string."\n");
}
private function writeln($string) {
$this->output->writeln($string);
$this->filesystem->appendToFile($this->rootlog.'cron.log', $string."\n");
}
}

View File

@ -57,6 +57,16 @@ class ChatController extends Controller
// Récupération des message du groupe // Récupération des message du groupe
$messages=$em->getRepository("CadolesWebsocketBundle:Message")->findBy(["group"=>$group],["submitdate"=>"DESC"],100); $messages=$em->getRepository("CadolesWebsocketBundle:Message")->findBy(["group"=>$group],["submitdate"=>"DESC"],100);
foreach($messages as $message) {
$haveread = ($message->getReaders()->contains($user));
$message->setHaveread($haveread);
if(!$haveread) {
$message->addReader($this->getUser());
$em->persist($message);
$em->flush();
}
}
// Création du formulaire // Création du formulaire
$form = $this->createForm(ChatType::class,null,[]); $form = $this->createForm(ChatType::class,null,[]);

View File

@ -59,6 +59,18 @@ class Message
protected $sees; protected $sees;
/* champs calculé non stocké en base */
private $haveread;
public function getHaveread()
{
return $this->haveread;
}
public function setHaveread($haveread) {
$this->haveread = $haveread;
return $this;
}
/** /**
* Constructor * Constructor
*/ */

View File

@ -23,6 +23,10 @@
background-color: #{{ colorbodyback }}; background-color: #{{ colorbodyback }};
color: #{{ colorbodyfont }}; color: #{{ colorbodyfont }};
} }
.message-toread {
font-weight:bold;
}
.msgavatar { .msgavatar {
float:left; float:left;
height:40px; height:40px;
@ -58,13 +62,18 @@
<input id="sendbymail" type="checkbox" id="item_essential" name="item[essential]" style="float:right" class='switch' ></input> <input id="sendbymail" type="checkbox" id="item_essential" name="item[essential]" style="float:right" class='switch' ></input>
<label class="custom-control-label" for="sendbymail">Envoyer par Mail ?</label> <label class="custom-control-label" for="sendbymail">Envoyer par Mail ?</label>
<a id="sendbtn" class="btn btn-success" style="margin-top:5px; width:100%; margin-bottom:15px">Envoyer</a> <a id="sendbtn" class="btn btn-success" style="margin-top:5px; width:100%; margin-bottom:15px">Envoyer</a>
<div id="useronline" style="margin-bottom:10px"></div>
</div> </div>
<div class="mychat"> <div class="mychat">
{% for message in messages %} {% for message in messages %}
<div id='message-{{message.id}}' class='message row'> {% set classread="" %}
{% if not message.haveread %}
{% set classread="message-toread" %}
{% endif %}
<div id='message-{{message.id}}' class='message row {{classread}}'>
<div class='msgavatar'> <div class='msgavatar'>
<img style='cursor:pointer' onclick='seeUser({{message.user.id}})' id='user_avatar_img' src='/{{ alias }}/uploads/avatar/{{message.user.avatar}}' class='avatar'><br> <img style='cursor:pointer' onclick='seeUser({{message.user.id}})' id='user_avatar_img' src='/{{ alias }}/uploads/avatar/{{message.user.avatar}}' class='avatar'><br>
{% if fgmanager or message.user == app.user %} {% if fgmanager or message.user == app.user %}
@ -222,16 +231,18 @@
], ],
height: 150 height: 150
}); });
},500); },1000);
$(".switch").bootstrapSwitch(); $(".switch").bootstrapSwitch();
$("#mymodal-sendmail").removeAttr("tabindex"); $("#mymodal-sendmail").removeAttr("tabindex");
websocket(); websocket();
/*
setInterval(function(){ setInterval(function(){
islive(); islive();
}, 15000); }, 15000);
*/
}); });
{% if websocket_activate and app.user %} {% if websocket_activate and app.user %}
@ -275,7 +286,7 @@
isdeco=false; isdeco=false;
if(payload.msg) { if(payload.msg) {
html ="<div id='message-"+payload.msg.id+"' class='message row'>"; html ="<div id='message-"+payload.msg.id+"' class='message row message-toread'>";
html+="<div class='msgavatar'>"; html+="<div class='msgavatar'>";
html+="<img style='pointer:cursor' onclick='seeUser("+payload.msg.userid+")' id='user_avatar_img' src='/{{ alias }}/uploads/avatar/"+payload.msg.avatar+"' class='avatar'><br>"; html+="<img style='pointer:cursor' onclick='seeUser("+payload.msg.userid+")' id='user_avatar_img' src='/{{ alias }}/uploads/avatar/"+payload.msg.avatar+"' class='avatar'><br>";
if(payload.msg.userid=={{app.user.id}} || '{{ fgmanager }}'=='1') { if(payload.msg.userid=={{app.user.id}} || '{{ fgmanager }}'=='1') {
@ -303,6 +314,28 @@
if(payload.alert) { if(payload.alert) {
alert(payload.alert); alert(payload.alert);
} }
if(payload.islive) {
if(payload.userid!={{app.user.id}}) {
console.log("Received message islive", payload.userid);
addOnline(payload.userid, payload.useravatar, payload.userlastname, payload.userfirstname);
{% if app.user.visible %}
event={mykey: "{{userkey}}", type: "meto", userid: {{app.user.id}}, useravatar: "{{app.user.avatar}}", userlastname: "{{ app.user.lastname}}", userfirstname: "{{ app.user.firstname }}" };
session.publish("websocket/channel/{{groupid}}", event);
{% endif %}
}
}
if(payload.meto) {
console.log("Received message meto", payload.userid, payload.useravatar, payload.userlastname, payload.userfirstname);
addOnline(payload.userid, payload.useravatar, payload.userlastname, payload.userfirstname);
}
if(payload.isdead) {
console.log("Received message isdead", payload.userid);
$("#online"+payload.userid).remove();
}
}); });
}); });
@ -472,11 +505,24 @@
function islive() { function islive() {
if(!session) return; if(!session) return;
isalive=true; isalive=true;
event={mykey: "{{userkey}}", type: "islive", userid: {{app.user.id}}};
{% if app.user.visible %}
addOnline({{app.user.id}}, "{{app.user.avatar}}", "{{app.user.lastname}}", "{{app.user.firstname}}");
event={mykey: "{{userkey}}", type: "islive", userid: {{app.user.id}}, useravatar: "{{app.user.avatar}}", userlastname: "{{app.user.lastname}}", userfirstname: "{{app.user.firstname}}" };
console.log("send islive"); console.log("send islive");
session.publish("websocket/channel/{{groupid}}", event); session.publish("websocket/channel/{{groupid}}", event);
{% endif %}
} }
function addOnline(userid, useravatar, userlastname, userfirstname) {
if(!$("#online"+userid).length) {
html = "<span id='online"+userid+"'>";
html+= "<img style='cursor:pointer; width:30px; height:30px; margin-right:5px;' onclick='seeUser("+userid+")' title='"+userlastname+" "+userfirstname+"' id='user_avatar_img' src='/{{ alias }}/uploads/avatar/"+useravatar+"' class='avatar'>";
html+= "</span>";
$("#useronline").append(html);
}
}
{% endif %} {% endif %}

View File

@ -52,9 +52,7 @@ class WebsocketTopic implements TopicInterface
public function onUnSubscribe(ConnectionInterface $connection, Topic $topic, WampRequest $request) public function onUnSubscribe(ConnectionInterface $connection, Topic $topic, WampRequest $request)
{ {
//this will broadcast the message to ALL subscribers of this topic. //this will broadcast the message to ALL subscribers of this topic.
$topic->broadcast(['log' => "Alive = ".$event["userid"]]); $topic->broadcast(['isdead' => 'isdead','userid'=>$connection->userId]);
$topic->broadcast(['log' => $connection->resourceId . " has left " . $topic->getId(). " || userid = ".$connection->userId]);
} }
@ -90,10 +88,25 @@ class WebsocketTopic implements TopicInterface
if($group&&$usergroup&&$user) { if($group&&$usergroup&&$user) {
if($event["type"]=="islive") { if($event["type"]=="islive") {
$topic->broadcast(['log' => "Alive = ".$event["userid"]]); $topic->broadcast([
"islive" => "islive",
"userid" => $event["userid"],
"useravatar" => $event["useravatar"],
"userlastname" => $event["userlastname"],
"userfirstname" => $event["userfirstname"],
]);
$connection->userId=$event["userid"]; $connection->userId=$event["userid"];
} }
if($event["type"]=="meto") {
$topic->broadcast([
"meto" => "meto",
"userid" => $event["userid"],
"useravatar" => $event["useravatar"],
"userlastname" => $event["userlastname"],
"userfirstname" => $event["userfirstname"],
]);
}
if($event["type"]=="add") { if($event["type"]=="add") {
if(array_key_exists("mail",$event)) { if(array_key_exists("mail",$event)) {