From 4f9bb3e2565151e8cc1c6ed409f15886f459b085 Mon Sep 17 00:00:00 2001 From: Pierre-Lin Bonnemaison <pierre.lin@free.fr> Date: Mon, 17 Aug 2015 02:30:09 +0200 Subject: [PATCH] =?UTF-8?q?Ajout=20du=20syst=C3=A8me=20de=20discussion=20e?= =?UTF-8?q?t=20fixe=20des=20quelques=20bugs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- controllers/discussions.php | 232 ++++++++++++++++++++++++++++++ controllers/internalConsole.php | 5 +- controllers/scheduleds.php | 11 +- controllers/smsAPI.php | 2 +- css/style.css | 100 +++++++++++++ js/custom.js | 55 +++++++ model/DataBase.php | 91 ++++++++++++ templates/discussions/default.php | 68 +++++++++ templates/discussions/show.php | 134 +++++++++++++++++ templates/internalIncs/nav.php | 3 + templates/scheduleds/add.php | 2 +- templates/scheduleds/edit.php | 2 +- 12 files changed, 699 insertions(+), 6 deletions(-) create mode 100755 controllers/discussions.php create mode 100755 templates/discussions/default.php create mode 100755 templates/discussions/show.php diff --git a/controllers/discussions.php b/controllers/discussions.php new file mode 100755 index 0000000..bba8d38 --- /dev/null +++ b/controllers/discussions.php @@ -0,0 +1,232 @@ +<?php + /** + * Page des discussions + */ + class discussions extends Controller + { + /** + * Cette fonction est appelée avant toute les autres : + * Elle vérifie que l'utilisateur est bien connecté + * @return void; + */ + public function before() + { + internalTools::verifyConnect(); + } + + /** + * Cette fonction retourne toutes les discussions, sous forme d'un tableau permettant l'administration de ces contacts + */ + public function byDefault() + { + //Creation de l'object de base de données + global $db; + + //Recupération des nombres des 4 panneaux d'accueil + $discussions = $db->getDiscussions(); + + foreach ($discussions as $key => $discussion) + { + if (!$contacts = $db->getFromTableWhere('contacts', ['number' => $discussion['number']])) + { + continue; + } + + $discussions[$key]['contact'] = $contacts[0]['name']; + } + + $this->render('discussions/default', array( + 'discussions' => $discussions, + )); + } + + /** + * Cette fonction permet d'afficher la discussion avec un numero + * @param string $number : La numéro de téléphone avec lequel on discute + */ + public function show ($number) + { + global $db; + + $contact = ''; + + if ($contacts = $db->getFromTableWhere('contacts', ['number' => $number])) + { + $contact = $contacts[0]['name']; + } + + $this->render('discussions/show', array( + 'number' => $number, + 'contact' => $contact, + )); + } + + /** + * Cette fonction récupère l'ensemble des messages pour un numéro, recçus, envoyés, en cours + * @param string $number : Le numéro cible + * @param string $transactionId : Le numéro unique de la transaction ajax (sert à vérifier si la requete doit être prise en compte) + */ + function getmessages($number, $transactionId) + { + global $db; + + $now = new DateTime(); + $now = $now->format('Y-m-d H:i:s'); + + $sendeds = $db->getFromTableWhere('sendeds', ['target' => $number], 'at'); + $receiveds = $db->getFromTableWhere('receiveds', ['send_by' => $number], 'at'); + $scheduleds = $db->getScheduledsBeforeDateForNumber($now, $number); + + $messages = []; + + foreach ($sendeds as $sended) + { + $messages[] = array( + 'date' => $sended['at'], + 'text' => $sended['content'], + 'type' => 'sended', + ); + } + + foreach ($receiveds as $received) + { + $messages[] = array( + 'date' => $received['at'], + 'text' => $received['content'], + 'type' => 'received', + ); + } + + foreach ($scheduleds as $scheduled) + { + $messages[] = array( + 'date' => $scheduled['at'], + 'text' => $scheduled['content'], + 'type' => 'inprogress', + ); + } + + //On va trier le tableau des messages + usort($messages, function($a, $b) { + return strtotime($a["date"]) - strtotime($b["date"]); + }); + + echo json_encode(['transactionId' => $transactionId, 'messages' => $messages]); + return true; + } + + /** + * Cette fonction permet d'envoyer facilement un sms à un numéro donné + * @param string $csrf : Le jeton csrf + * @param string $_POST['content'] : Le contenu du SMS + * @param string $_POST['numbers'] : Un tableau avec le numero des gens auxquel envoyer le sms + * @return json : Le statut de l'envoi + */ + function send ($csrf) + { + global $db; + $return = ['success' => true, 'message' => '']; + + //On vérifie que le jeton csrf est bon + if (!internalTools::verifyCSRF($csrf)) + { + $return['success'] = false; + $return['message'] = 'Jeton CSRF invalide'; + echo json_encode($return); + return false; + } + + $now = new DateTime(); + $now = $now->format('Y-m-d H:i:s'); + + $_POST['date'] = $now; + + $scheduleds = new scheduleds(); + if (!$scheduleds->create('', true, true)) + { + $return['success'] = false; + $return['message'] = 'Impossible de créer le SMS'; + echo json_encode($return); + return false; + } + + $return['id'] = $_SESSION['discussion_wait_progress'][count($_SESSION['discussion_wait_progress']) - 1]; + + echo json_encode($return); + return true; + } + + /** + * Cette fonction retourne les id des sms qui sont envoyés + * @return json : Tableau des ids des sms qui sont envoyés + */ + function checksendeds () + { + global $db; + + $_SESSION['discussion_wait_progress'] = isset($_SESSION['discussion_wait_progress']) ? $_SESSION['discussion_wait_progress'] : []; + + $scheduleds = $db->getScheduledsIn($_SESSION['discussion_wait_progress']); + + //On va chercher à chaque fois si on a trouvé le sms. Si ce n'est pas le cas c'est qu'il a été envoyé + $sendeds = []; + foreach ($_SESSION['discussion_wait_progress'] as $key => $id) + { + $found = false; + foreach ($scheduleds as $scheduled) + { + if ($id == $scheduled['id']) + { + $found = true; + } + } + + if (!$found) + { + unset($_SESSION['discussion_wait_progress'][$key]); + $sendeds[] = $id; + } + } + + echo json_encode($sendeds); + return true; + } + + /** + * Cette fonction retourne les messages reçus pour un numéro après la date $_SESSION['discussion_last_checkreceiveds'] + * @param string $number : Le numéro de téléphone pour lequel on veux les messages + * @return json : Un tableau avec les messages + */ + function checkreceiveds ($number) + { + global $db; + + $now = new DateTime(); + $now = $now->format('Y-m-d H:i'); + + $_SESSION['discussion_last_checkreceiveds'] = isset($_SESSION['discussion_last_checkreceiveds']) ? $_SESSION['discussion_last_checkreceiveds'] : $now; + + $receiveds = $db->getReceivedsSinceForNumberOrderByDate($_SESSION['discussion_last_checkreceiveds'], $number); + + //On va gérer le cas des messages en double en stockant ceux déjà reçus et en eliminant les autres + $_SESSION['discussion_already_receiveds'] = isset($_SESSION['discussion_already_receiveds']) ? $_SESSION['discussion_already_receiveds'] : []; + + foreach ($receiveds as $key => $received) + { + //Sms jamais recu + if (array_search($received['id'], $_SESSION['discussion_already_receiveds']) === false) + { + $_SESSION['discussion_already_receiveds'][] = $received['id']; + continue; + } + + //Sms déjà reçu => on le supprime des resultats + unset($receiveds[$key]); + } + + //On met à jour la date de dernière verif + $_SESSION['discussion_last_checkreceiveds'] = $now; + + echo json_encode($receiveds); + } + } diff --git a/controllers/internalConsole.php b/controllers/internalConsole.php index 4e10031..56996ab 100755 --- a/controllers/internalConsole.php +++ b/controllers/internalConsole.php @@ -134,7 +134,10 @@ $now = new DateTime(); $now = $now->format('Y-m-d H:i:s'); //On peut maintenant ajouter le SMS - $db->insertIntoTable('sendeds', ['at' => $now, 'number' => $number, 'content' => $scheduled['content']]); + if (!$db->insertIntoTable('sendeds', ['at' => $now, 'target' => $number, 'content' => $scheduled['content']])) + { + echo 'Impossible d\'inserer le sms pour le numero ' . $number . "\n"; + } $id_sended = $db->lastId(); //Commande qui envoie le SMS diff --git a/controllers/scheduleds.php b/controllers/scheduleds.php index 0b05039..dfca54e 100755 --- a/controllers/scheduleds.php +++ b/controllers/scheduleds.php @@ -102,7 +102,7 @@ * @param string $_POST['groups'] : Un tableau avec les ids des groupes auxquels envoyer le sms * @return boolean; */ - public function create($csrf = '', $api = false) + public function create($csrf = '', $api = false, $discussion = false) { if (!$api) { @@ -145,7 +145,7 @@ return false; } - if (!internalTools::validateDate($date, 'Y-m-d H:i')) + if (!internalTools::validateDate($date, 'Y-m-d H:i:s')) { if (!$api) { @@ -167,6 +167,13 @@ } $id_scheduled = $db->lastId(); + + if ($discussion) + { + $_SESSION['discussion_wait_progress'] = isset($_SESSION['discussion_wait_progress']) ? $_SESSION['discussion_wait_progress'] : []; + $_SESSION['discussion_wait_progress'][] = $id_scheduled; + } + $db->insertIntoTable('events', ['type' => 'SCHEDULED_ADD', 'text' => 'Ajout d\'un SMS pour le ' . $date]); $errors = false; diff --git a/controllers/smsAPI.php b/controllers/smsAPI.php index 3d8e3f7..1c3b05d 100755 --- a/controllers/smsAPI.php +++ b/controllers/smsAPI.php @@ -120,7 +120,7 @@ $_POST['groups'] = $groups; $scheduleds = new scheduleds(); - $success = $scheduleds->create(true); + $success = $scheduleds->create('', true); if (!$success) { diff --git a/css/style.css b/css/style.css index d8314a7..a7df0e9 100755 --- a/css/style.css +++ b/css/style.css @@ -88,3 +88,103 @@ footer color: #fff; background-color: #000; } + +.goto:hover +{ + cursor: pointer; +} + +/** DISCUSSION **/ +.table-discussions tbody tr:hover +{ + cursor: pointer; + background-color: #999; +} + +.discussion-container +{ + overflow: auto; +} + +.discussion-message +{ + border-radius: 5px; + display: inline-block; + max-width: 90%; + padding: 10px; + position: relative; +} + +.discussion-message-text +{ + font-size: 1.3em; +} + +.discussion-message-date +{ + font-size: 0.9em; +} + +.message-container +{ + margin-bottom: 10px; +} + +.message-received +{ + background-color: #1abc9c; + float: left; + color: #fff; +} + +.message-sended +{ + background-color: #ddd; + float: right; +} + +.message-received .discussion-message-date +{ + color: #f0f0f0; +} + +.message-sended .discussion-message-date +{ + text-align: right; + color: #888; +} + +.message-input +{ + width: 100%; + max-width: 100%; + background-color: #ddd; +} + +.message-input textarea +{ + width: 100%; + resize: none; + height: 6em; +} + +.message-input button +{ + float: right; + margin-top: 8px; +} + +.message-in-progress-hover +{ + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: 900; + opacity: 0.7; + background-color: #fff; + font-size: 20px; + text-align: center; + padding-top: 10px; +} diff --git a/js/custom.js b/js/custom.js index f404c51..12261fd 100755 --- a/js/custom.js +++ b/js/custom.js @@ -33,8 +33,63 @@ function verifReceived() }); } +/** + * Cette fonction permet de scroller au dernier message + */ +function scrollDownDiscussion() +{ + jQuery('.discussion-container').animate({scrollTop: 1000000}); +} jQuery(document).ready(function() { var verifReceivedInterval = setInterval(verifReceived, 10000); + + jQuery('body').on('click', '.goto', function (e) { + e.preventDefault(); + if (jQuery(this).attr('url')) + { + if (jQuery(this).attr('target')) + { + window.open(jQuery(this).attr('url'), jQuery(this).attr('target')); + } + else + { + window.location = jQuery(this).attr('url'); + } + } + }); + + jQuery('body').on('submit', '.send-message-discussion', function (e) + { + e.preventDefault(); + + var form = jQuery(this); + var message = form.find('textarea').val(); + var formData = new FormData(form[0]); + jQuery('.discussion-container').find('#send-message-spiner').remove(); + jQuery('.discussion-container').append('<div class="text-center" id="send-message-spiner"><i class="fa fa-spinner fa-spin"></i></div>'); + scrollDownDiscussion(); + jQuery.ajax({ + url: form.attr('action'), + type: form.attr('method'), + data: formData, + contentType: false, + processData: false, + dataType: "json", + success: function (data) + { + if (!data.success) + { + showMessage(data.message.replace(/</g, "<").replace(/>/g, ">"), 0); + jQuery('.discussion-container').find('#send-message-spiner').remove(); + } + } + }).done(function() + { + form.trigger("reset"); + }); + }); + + scrollDownDiscussion(); }); diff --git a/model/DataBase.php b/model/DataBase.php index 6ebb585..5557006 100755 --- a/model/DataBase.php +++ b/model/DataBase.php @@ -115,6 +115,46 @@ return $this->runQuery($query, $params); } + /** + * Récupère les SMS reçus depuis une date pour un numero + * @param $date : La date depuis laquelle on veux les SMS (au format 2014-10-25 20:10:05) + * @param $number : Le numéro + * @return array : Tableau avec tous les SMS depuis la date + */ + public function getReceivedsSinceForNumberOrderByDate($date, $number) + { + $query = " + SELECT * + FROM receiveds + WHERE at > STR_TO_DATE(:date, '%Y-%m-%d %h:%i:%s') + AND send_by = :number + ORDER BY at ASC + "; + + $params = array( + 'date' => $date, + 'number' => $number + ); + + return $this->runQuery($query, $params); + } + + /** + * Récupère les SMS reçus groupé par numéro et trié par date + * @return array : Le tablea avec les sms et la date + */ + public function getDiscussions() + { + $query = " + SELECT MAX(at) as at, number + FROM (SELECT at, target as number FROM sendeds UNION (SELECT at, send_by as number FROM receiveds)) as discussions + GROUP BY number + ORDER BY at + "; + + return $this->runQuery($query); + } + /********************************/ /* PARTIE DES REQUETES CONTACTS */ /********************************/ @@ -382,6 +422,57 @@ return $this->runQuery($query, $params, self::ROWCOUNT); } + /** + * Cette fonction retourne les sms programmés pour un numéro donné et avant une date + * @param string $date : La date avant laquel on veux les numéros (format yyyy-mm-dd hh:mm:ss) + * @param string $number : Le numéro cible + * @return array : Les scheduleds correspondants + */ + public function getScheduledsBeforeDateForNumber($date, $number) + { + $query = " + SELECT * + FROM scheduleds + WHERE at <= :date + AND ( + id IN ( + SELECT id_scheduled + FROM scheduleds_numbers + WHERE number = :number + ) + OR id IN ( + SELECT id_scheduled + FROM scheduleds_contacts + WHERE id_contact IN ( + SELECT id + FROM contacts + WHERE number = :number + ) + ) + OR id IN ( + SELECT id_scheduled + FROM scheduleds_groups + WHERE id_group IN ( + SELECT id_group + FROM groups_contacts + WHERE id_contact IN ( + SELECT id + FROM contacts + WHERE number = :number + ) + ) + ) + ) + "; + + $params = array( + 'date' => $date, + 'number' => $number, + ); + + return $this->runQuery($query, $params); + } + /********************************/ /* PARTIE DES REQUETES COMMANDS */ /********************************/ diff --git a/templates/discussions/default.php b/templates/discussions/default.php new file mode 100755 index 0000000..52a17fe --- /dev/null +++ b/templates/discussions/default.php @@ -0,0 +1,68 @@ +<?php + //Template dashboard + $incs = new internalIncs(); + $incs->head('Discussions - Show All'); +?> +<div id="wrapper"> +<?php + $incs->nav('discussions'); +?> + <div id="page-wrapper"> + <div class="container-fluid"> + <!-- Page Heading --> + <div class="row"> + <div class="col-lg-12"> + <h1 class="page-header"> + Dashboard <small>Discussions</small> + </h1> + <ol class="breadcrumb"> + <li> + <i class="fa fa-dashboard"></i> <a href="<?php echo $this->generateUrl('dashboard'); ?>">Dashboard</a> + </li> + <li class="active"> + <i class="fa fa-comments-o"></i> Discussions + </li> + </ol> + </div> + </div> + <!-- /.row --> + + <div class="row"> + <div class="col-lg-12"> + <div class="panel panel-default"> + <div class="panel-heading"> + <h3 class="panel-title"><i class="fa fa-comments-o fa-fw"></i> Liste des discussions</h3> + </div> + <div class="panel-body"> + <div class="table-responsive"> + <table class="table table-bordered table-hover table-striped" id="table-discussions"> + <thead> + <tr> + <th>Date du dernier message</th> + <th>Numéro</th> + </tr> + </thead> + <tbody> + <?php + foreach ($discussions as $discussion) + { + ?> + <tr class="goto" url="<?php secho($this->generateUrl('discussions', 'show', [$discussion['number']])); ?>"> + <td><?php secho($discussion['at']); ?></td> + <td><?php secho(isset($discussion['contact']) ? $discussion['contact'] . ' (' . $discussion['number'] . ')' : $discussion['number']); ?></td> + </tr> + <?php + } + ?> + </tbody> + </table> + </div> + </div> + </div> + </div> + </div> + </div> + </div> +</div> +<?php + $incs->footer(); diff --git a/templates/discussions/show.php b/templates/discussions/show.php new file mode 100755 index 0000000..67ce0d0 --- /dev/null +++ b/templates/discussions/show.php @@ -0,0 +1,134 @@ +<?php + //Template dashboard + $incs = new internalIncs(); + $incs->head('Discussions - Show All'); +?> +<div id="wrapper"> +<?php + $incs->nav('discussions'); +?> + <div id="page-wrapper"> + <div class="container-fluid"> + <!-- Page Heading --> + <div class="row"> + <div class="col-lg-12"> + <h1 class="page-header"> + Discussion <small><?php secho($contact ? $contact . ' (' . $number . ')' : $number); ?></small> + </h1> + <ol class="breadcrumb"> + <li> + <i class="fa fa-dashboard"></i> <a href="<?php echo $this->generateUrl('dashboard'); ?>">Dashboard</a> + </li> + <li> + <i class="fa fa-comments-o"></i> <a href="<?php echo $this->generateUrl('discussions'); ?>">Discussions</a> + </li> + <li class="active"> + <?php secho($number); ?> + </li> + </ol> + </div> + </div> + <!-- /.row --> + + <div class="row"> + <div class="col-lg-12 discussion-container"> + <div class="text-center"><i class="fa fa-spinner fa-spin"></i></div> + </div> + <div class="col-lg-12 message-input-container"> + <div class="discussion-message message-input"> + <form class="send-message-discussion" action="<?php secho($this->generateUrl('discussions', 'send', [$_SESSION['csrf']])); ?>" method="POST"> + <textarea name="content" placeholder="Envoyer un message..."></textarea> + <input type="hidden" name="numbers[]" value="<?php secho($number); ?>" /> + <button class="btn" ><span class="fa fa-fw fa-send-o"></span> Envoyer</button> + </form> + </div> + </div> + </div> + </div> + </div> +</div> +<script> + jQuery(document).ready(function () { + /** + * Cette fonction vérifie régulièrement les sms pour mettre à jour l'affichage + */ + function getmessages () + { + ajaxTransactionId = Date.now(); + jQuery.getJSON(HTTP_PWD + "/discussions/getmessages/<?php echo htmlspecialchars(urlencode($number)); ?>/" + ajaxTransactionId , function( data ) { + if (data.transactionId != ajaxTransactionId) + { + return false; + } + + jQuery('.discussion-container').html(''); + + $.each(data.messages, function(key, message) { + + switch (message.type) + { + case 'received' : + var texte = '' + + '<div class="clearfix message-container">' + + '<div class="discussion-message message-received">' + + '<div class="discussion-message-text">' + message.text.replace(/</g, "<").replace(/>/g, ">") + '</div>' + + '<div class="discussion-message-date">' + message.date.replace(/</g, "<").replace(/>/g, ">") + '</div>' + + '</div>' + + '</div>'; + break; + case 'sended' : + var texte = '' + + '<div class="clearfix message-container">' + + '<div class="discussion-message message-sended">' + + '<div class="discussion-message-text">' + message.text.replace(/</g, "<").replace(/>/g, ">") + '</div>' + + '<div class="discussion-message-date">' + message.date.replace(/</g, "<").replace(/>/g, ">") + '</div>' + + '</div>' + + '</div>'; + break; + case 'inprogress' : + var texte = '' + + '<div class="clearfix message-container">' + + '<div class="discussion-message message-sended">' + + '<div class="message-in-progress-hover"><i class="fa fa-spinner fa-spin"></i></div>' + + '<div class="discussion-message-text">' + message.text.replace(/</g, "<").replace(/>/g, ">") + '</div>' + + '<div class="discussion-message-date">' + message.date.replace(/</g, "<").replace(/>/g, ">") + '</div>' + + '</div>' + + '</div>'; + break; + default : + var texte = ''; + break; + } + + jQuery('.discussion-container').append(texte); + }); + scrollDownDiscussion(); + }); + } + + /** + * Cette fonction permet de fixer la taille de la fenetre de discussion + */ + function fullHeightDiscussion() + { + var containerPosition = jQuery('.discussion-container').position(); + var windowHeight = jQuery(window).height(); + var messageInputContainer = jQuery('.message-input-container').outerHeight(); + var footerHeight = jQuery('footer').outerHeight(); + + var containerHeight = Math.floor(windowHeight - (containerPosition.top + footerHeight * 2 + messageInputContainer)); + + jQuery('.discussion-container').outerHeight(containerHeight); + } + + fullHeightDiscussion(); + + jQuery(window).on('resize', function () { + fullHeightDiscussion(); + }); + + var getmessagesInterval = setInterval(getmessages, 2500); + }); +</script> +<?php + $incs->footer(); diff --git a/templates/internalIncs/nav.php b/templates/internalIncs/nav.php index 84e78de..49ba288 100755 --- a/templates/internalIncs/nav.php +++ b/templates/internalIncs/nav.php @@ -34,6 +34,9 @@ <li <?php echo $page == 'scheduleds' ? 'class="active"' : ''; ?>> <a href="<?php echo $this->generateUrl('scheduleds'); ?>"><i class="fa fa-fw fa-envelope"></i> SMS</a> </li> + <li <?php echo $page == 'discussions' ? 'class="active"' : ''; ?>> + <a href="<?php echo $this->generateUrl('discussions'); ?>"><i class="fa fa-fw fa-comments"></i> Discussions</a> + </li> <li <?php echo $page == 'commands' ? 'class="active"' : ''; ?>> <a href="<?php echo $this->generateUrl('commands'); ?>"><i class="fa fa-fw fa-terminal"></i> Commandes</a> </li> diff --git a/templates/scheduleds/add.php b/templates/scheduleds/add.php index 116956f..8eb838a 100755 --- a/templates/scheduleds/add.php +++ b/templates/scheduleds/add.php @@ -73,7 +73,7 @@ { jQuery('.form-datetime').datetimepicker( { - format: 'yyyy-mm-dd hh:ii', + format: 'yyyy-mm-dd hh:ii:ss', autoclose: true, minuteStep: 1, language: 'fr' diff --git a/templates/scheduleds/edit.php b/templates/scheduleds/edit.php index afd22fb..5a0cc8a 100755 --- a/templates/scheduleds/edit.php +++ b/templates/scheduleds/edit.php @@ -102,7 +102,7 @@ { jQuery('.form-datetime').datetimepicker( { - format: 'yyyy-mm-dd hh:ii', + format: 'yyyy-mm-dd hh:ii:ss', autoclose: true, minuteStep: 1, language: 'fr'