From 80b06d65cdf811cfb59c340db9f021e78ac59cec Mon Sep 17 00:00:00 2001
From: Olivier PEREZ <olivier@olivierperez.fr>
Date: Wed, 17 Dec 2014 13:17:08 +0100
Subject: [PATCH] Some work on vote saving

---
 .gitignore                                    | 10 +-
 app/classes/Framadate/FramaDB.php             | 24 ++---
 app/classes/Framadate/Message.php             | 15 +++
 .../Framadate/Services/InputService.php       | 28 ++++++
 .../Framadate/Services/PollService.php        | 83 ++++++++++++++++
 app/inc/init.php                              |  1 -
 app/inc/studs.inc.php                         | 10 --
 studs.php                                     | 98 ++++++++-----------
 tpl/footer.tpl                                |  2 +-
 tpl/studs.tpl                                 | 16 +--
 10 files changed, 192 insertions(+), 95 deletions(-)
 create mode 100644 app/classes/Framadate/Message.php
 create mode 100644 app/classes/Framadate/Services/InputService.php
 create mode 100644 app/classes/Framadate/Services/PollService.php
 delete mode 100644 app/inc/studs.inc.php

diff --git a/.gitignore b/.gitignore
index 90c4c5b9..9181863b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,5 @@
 .htaccess
-admin/.htaccess
-admin/.htpasswd
+.htpasswd
 admin/logs_studs.txt
 composer.lock
 composer.phar
@@ -8,7 +7,10 @@ framanav
 nav
 app/inc/constants.php
 vendor
-.settings/
-.project
 cache/
 tpl_c/
+
+.settings/
+.project
+.idea/
+*.iml
diff --git a/app/classes/Framadate/FramaDB.php b/app/classes/Framadate/FramaDB.php
index 12fc08e1..184f7b6a 100644
--- a/app/classes/Framadate/FramaDB.php
+++ b/app/classes/Framadate/FramaDB.php
@@ -39,19 +39,13 @@ class FramaDB
     }
 
     function findPollById($poll_id) {
+        $prepared = $this->prepare('SELECT * FROM sondage WHERE sondage.poll_id = ?');
 
-        // Open database
-        if (preg_match(';^[\w\d]{16}$;i', $poll_id)) {
-            $prepared = $this->prepare('SELECT * FROM sondage WHERE sondage.poll_id = ?');
+        $prepared->execute([$poll_id]);
+        $poll = $prepared->fetch();
+        $prepared->closeCursor();
 
-            $prepared->execute([$poll_id]);
-            $poll = $prepared->fetch();
-            $prepared->closeCursor();
-
-            return $poll;
-        }
-
-        return null;
+        return $poll;
     }
 
     function allCommentsByPollId($poll_id) {
@@ -72,15 +66,15 @@ class FramaDB
         return $prepared->fetchAll();
     }
 
-    function insertVote($name, $poll_id, $votes) {
-        $prepared = $this->prepare('INSERT INTO user_studs (nom,id_sondage,reponses) VALUES (?,?,?)');
-        $prepared->execute([$name, $poll_id, $votes]);
+    function insertVote($poll_id, $name, $choices) {
+        $prepared = $this->prepare('INSERT INTO user_studs (id_sondage,nom,reponses) VALUES (?,?,?)');
+        $prepared->execute([$poll_id, $name, $choices]);
 
         $newVote = new \stdClass();
         $newVote->id_sondage = $poll_id;
         $newVote->id_users = $this->pdo->lastInsertId();
         $newVote->nom = $name;
-        $newVote->reponse = $votes;
+        $newVote->reponse = $choices;
 
         return $newVote;
     }
diff --git a/app/classes/Framadate/Message.php b/app/classes/Framadate/Message.php
new file mode 100644
index 00000000..b14c3767
--- /dev/null
+++ b/app/classes/Framadate/Message.php
@@ -0,0 +1,15 @@
+<?php
+namespace Framadate;
+
+class Message {
+
+    var $type;
+    var $message;
+
+    function __construct($type, $message) {
+        $this->type = $type;
+        $this->message = $message;
+    }
+
+}
+ 
\ No newline at end of file
diff --git a/app/classes/Framadate/Services/InputService.php b/app/classes/Framadate/Services/InputService.php
new file mode 100644
index 00000000..49a7c391
--- /dev/null
+++ b/app/classes/Framadate/Services/InputService.php
@@ -0,0 +1,28 @@
+<?php
+namespace Framadate\Services;
+
+/**
+ * This class helps to clean all inputs from the users or external services.
+ */
+class InputService {
+
+    function __construct() {}
+
+    /**
+     * This method filter an array calling "filter_var" on each items.
+     * Only items validated are added at their own indexes, the others are not returned.
+     */
+    function filterArray($arr, $type, $options) {
+        $newArr = [];
+
+        foreach($arr as $id=>$item) {
+            $item = filter_var($item, $type, $options);
+            if ($item !== false) {
+                $newArr[$id] = $item;
+            }
+        }
+
+        return $newArr;
+    }
+
+}
\ No newline at end of file
diff --git a/app/classes/Framadate/Services/PollService.php b/app/classes/Framadate/Services/PollService.php
new file mode 100644
index 00000000..51fa3e7b
--- /dev/null
+++ b/app/classes/Framadate/Services/PollService.php
@@ -0,0 +1,83 @@
+<?php
+namespace Framadate\Services;
+
+class PollService {
+
+    private $connect;
+
+    function __construct($connect) {
+        $this->connect = $connect;
+    }
+
+    function findById($poll_id) {
+        if (preg_match('/^[\w\d]{16}$/i', $poll_id)) {
+            return $this->connect->findPollById($poll_id);
+        }
+
+        return null;
+    }
+
+    function allCommentsByPollId($poll_id) {
+        return $this->connect->allCommentsByPollId($poll_id);
+    }
+
+    function allUserVotesByPollId($poll_id) {
+        return $this->connect->allUserVotesByPollId($poll_id);
+    }
+
+    function allSlotsByPollId($poll_id) {
+        return $this->connect->allSlotsByPollId($poll_id);
+    }
+
+    public function updateVote($poll_id, $vote_id, $choices) {
+        $choices = implode($choices);
+        return $this->connect->updateVote($poll_id, $vote_id, $choices);
+    }
+
+    function addVote($poll_id, $name, $choices) {
+        $choices = implode($choices);
+        return $this->connect->insertVote($poll_id, $name, $choices);
+    }
+
+    function computeBestMoments($votes) {
+        $result = [];
+        foreach ($votes as $vote) {
+            $choices = str_split($vote->reponses);
+            foreach ($choices as $i=>$choice) {
+                if (empty($result[$i])) {
+                    $result[$i] = 0;
+                }
+                if ($choice == 2) {
+                    $result[$i]++;
+                }
+            }
+        }
+        return $result;
+    }
+
+    function splitSlots($slots) {
+        $splitted = array();
+        foreach ($slots as $slot) {
+            $ex = explode('@', $slot->sujet);
+            $obj = new \stdClass();
+            $obj->day = $ex[0];
+            $obj->moments = explode(',', $ex[1]);
+
+            $splitted[] = $obj;
+        }
+        return $splitted;
+    }
+
+    function splitVotes($votes) {
+        $splitted = array();
+        foreach ($votes as $vote) {
+            $obj = new \stdClass();
+            $obj->id = $vote->id_users;
+            $obj->name = $vote->nom;
+            $obj->choices = str_split($vote->reponses);
+
+            $splitted[] = $obj;
+        }
+        return $splitted;
+    }
+}
diff --git a/app/inc/init.php b/app/inc/init.php
index 5051953b..2989a0ec 100644
--- a/app/inc/init.php
+++ b/app/inc/init.php
@@ -24,7 +24,6 @@ if (ini_get('date.timezone') == '') {
 }
 include_once __DIR__ . '/constants.php';
 include_once __DIR__ . '/i18n.php';
-include_once __DIR__ . '/studs.inc.php';
 
 // Autoloading of dependencies with Composer
 require_once __DIR__ . '/../../vendor/autoload.php';
diff --git a/app/inc/studs.inc.php b/app/inc/studs.inc.php
deleted file mode 100644
index 24f81a3b..00000000
--- a/app/inc/studs.inc.php
+++ /dev/null
@@ -1,10 +0,0 @@
-<?php
-
-function countStuds($subjects)
-{
-    $nb = 0;
-    foreach($subjects as $subject) {
-        $nb += substr_count($subject->sujet, ',')+1;
-    }
-    return $nb;
-}
\ No newline at end of file
diff --git a/studs.php b/studs.php
index f638ac59..8f618067 100644
--- a/studs.php
+++ b/studs.php
@@ -17,59 +17,21 @@
  * Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
  */
 use Framadate\Services\PollService;
+use Framadate\Services\InputService;
 use Framadate\Utils;
+use Framadate\Message;
 
 include_once __DIR__ . '/app/inc/init.php';
 
-/* Functions */
+/* Variables */
 /* --------- */
-
-function split_slots($slots) {
-    $splitted = array();
-    foreach ($slots as $slot) {
-        $ex = explode('@', $slot->sujet);
-        $obj = new \stdClass();
-        $obj->day = $ex[0];
-        $obj->moments = explode(',', $ex[1]);
-
-        $splitted[] = $obj;
-    }
-    return $splitted;
-}
-
-function split_votes($votes) {
-    $splitted = array();
-    foreach ($votes as $vote) {
-        $obj = new \stdClass();
-        $obj->id = $vote->id_users;
-        $obj->name = $vote->nom;
-        $obj->choices = str_split($vote->reponses);
-
-        $splitted[] = $obj;
-    }
-    return $splitted;
-}
-
-function computeBestMoments($votes) {
-    $result = [];
-    foreach ($votes as $vote) {
-        $choices = str_split($vote->reponses);
-        foreach ($choices as $i=>$choice) {
-            if (empty($result[$i])) {
-                $result[$i] = 0;
-            }
-            if ($choice == 2) {
-                $result[$i]++;
-            }
-        }
-    }
-    return $result;
-}
+$message = null;
 
 /* Services */
 /*----------*/
 
 $pollService = new PollService($connect);
+$inputService = new InputService();
 
 /* PAGE */
 /* ---- */
@@ -78,7 +40,6 @@ if(!empty($_GET['poll'])) {
     $poll_id = filter_input(INPUT_GET, 'poll', FILTER_VALIDATE_REGEXP, ['options'=>['regexp'=>'/^[a-z0-9]+$/']]);
 }
 
-
 $poll = $pollService->findById($poll_id);
 
 if (!$poll) {
@@ -96,27 +57,47 @@ if (!empty($_POST['edit_vote'])) {
 }
 
 
+// Something to save (edit or add)
 if (!empty($_POST['save'])) { // Save edition of an old vote
     $editedVote = filter_input(INPUT_POST, 'save', FILTER_VALIDATE_INT);
-    $newChoices = [];
+    $choices = $inputService->filterArray($_POST['choices'], FILTER_VALIDATE_REGEXP, ['options'=>['regexp'=>'/^[012]$/']]);
+
+    if (empty($name)) {
+        $message = new Message('danger', _('Name is incorrect.'));
+    }
+    if (count($choices) != count($_POST['choices'])) {
+        $message = new Message('danger', _('There is a problem with your choices.'));
+    }
 
-    // TODO Do this verification into a Service (maybe called 'InputService')
-    foreach($_POST['choices'] as $id=>$choice) {
-        $choice = filter_var($choice, FILTER_VALIDATE_REGEXP, ['options'=>['regexp'=>'/^[012]$/']]);
-        if ($choice !== false) {
-            $newChoices[$id] = $choice;
+    if ($message == null) {
+        // Update vote
+        $result = $pollService->updateVote($poll_id, $editedVote, $choices);
+        if ($result) {
+            $message = new Message('success', _('Update vote successfully!'));
+        } else {
+            $message = new Message('danger', _('Update vote failed!'));
         }
     }
+} elseif (isset($_POST['save'])) { // Add a new vote
+    $name = filter_input(INPUT_POST, 'name', FILTER_VALIDATE_REGEXP, ['options'=>['regexp'=>'/^[a-z0-9_ -]+$/i']]);
+    $choices = $inputService->filterArray($_POST['choices'], FILTER_VALIDATE_REGEXP, ['options'=>['regexp'=>'/^[012]$/']]);
+
+    if (empty($name)) {
+        $message = new Message('danger', _('Name is incorrect.'));
+    }
+    if (count($choices) != count($_POST['choices'])) {
+        $message = new Message('danger', _('There is a problem with your choices.'));
+    }
 
-    if (count($newChoices) == count($_POST['choices'])) {
-        $result = $pollService->updatePoll($poll_id, $editedVote, $newChoices);
+    if ($message == null) {
+        // Add vote
+        $result = $pollService->addVote($poll_id, $name, $choices);
         if ($result) {
-            $message = ['type'=>'success', 'message'=>_('Update vote successfully!')];
+            $message = new Message('success', _('Update vote successfully!'));
         } else {
-            $message = ['type'=>'success', 'message'=>_('Update vote successfully!')];
+            $message = new Message('danger', _('Update vote failed!'));
         }
     }
-} elseif (isset($_POST[''])) { // Add a new vote
 }
 
 // Retrieve data
@@ -129,11 +110,12 @@ $comments = $pollService->allCommentsByPollId($poll_id);
 $smarty->assign('poll_id', $poll_id);
 $smarty->assign('poll', $poll);
 $smarty->assign('title', _('Poll') . ' - ' . $poll->title);
-$smarty->assign('slots', split_slots($slots));
-$smarty->assign('votes', split_votes($votes));
-$smarty->assign('best_moments', computeBestMoments($votes));
+$smarty->assign('slots', $pollService->splitSlots($slots));
+$smarty->assign('votes', $pollService->splitVotes($votes));
+$smarty->assign('best_moments', $pollService->computeBestMoments($votes));
 $smarty->assign('comments', $comments);
 $smarty->assign('editingVoteId', $editingVoteId);
+$smarty->assign('message', $message);
 
 //Utils::debug(computeBestMoments($votes));exit;
 
diff --git a/tpl/footer.tpl b/tpl/footer.tpl
index af3f5eb7..0313ca55 100644
--- a/tpl/footer.tpl
+++ b/tpl/footer.tpl
@@ -1,4 +1,4 @@
-		</main>
+        </main>
     </div> <!-- .container -->
 </body>
 </html>
\ No newline at end of file
diff --git a/tpl/studs.tpl b/tpl/studs.tpl
index 57d2f866..1bc08ff7 100644
--- a/tpl/studs.tpl
+++ b/tpl/studs.tpl
@@ -5,6 +5,10 @@
 
     {* Global informations about the current poll *}
 
+    {if !empty($message)}
+    <div class="alert alert-{$message->type}" role="alert">{$message->message}</div>
+    {/if}
+
     <div class="jumbotron">
         <div class="row">
             <div class="col-md-7">
@@ -12,7 +16,7 @@
             </div>
             <div class="col-md-5">
                 <div class="btn-group pull-right">
-                    <button onclick="javascript:print(); return false;" class="btn btn-default"><span class="glyphicon glyphicon-print"></span>{_('Print')}</button>
+                    <button onclick="print(); return false;" class="btn btn-default"><span class="glyphicon glyphicon-print"></span>{_('Print')}</button>
                     <a href="{$SERVER_URL}export.php?poll={$poll_id}&mode=csv" class="btn btn-default"><span class="glyphicon glyphicon-download-alt"></span>{_('Export to CSV')}</a>
                 </div>
             </div>
@@ -172,7 +176,7 @@
                     <td class="bg-info" style="padding:5px">
                         <div class="input-group input-group-sm">
                             <span class="input-group-addon"><span class="glyphicon glyphicon-user"></span></span>
-                            <input type="text" id="nom" name="nom" class="form-control" title="{_('Your name')}" placeholder="{_('Your name')}" />
+                            <input type="text" id="name" name="name" class="form-control" title="{_('Your name')}" placeholder="{_('Your name')}" />
                         </div>
                     </td>
                     {$i = 0}
@@ -181,19 +185,19 @@
                     <td class="bg-info" headers="M{$headersM[$i]} D{$headersD[$i]} H{$i}">
                         <ul class="list-unstyled choice">
                             <li class="yes">
-                                <input type="radio" id="y-choice-{$i}" name="choice{$i}" value="2" />
+                                <input type="radio" id="y-choice-{$i}" name="choices[{$i}]" value="2" />
                                 <label class="btn btn-default btn-xs" for="y-choice-{$i}" title="{_('Vote yes for')} {$slot->day|date_format:$date_format.txt_short} - {$moment}">
                                     <span class="glyphicon glyphicon-ok"></span><span class="sr-only">{_('Yes')}</span>
                                 </label>
                             </li>
                             <li class="ifneedbe">
-                                <input type="radio" id="i-choice-{$i}" name="choice{$i}" value="1" />
+                                <input type="radio" id="i-choice-{$i}" name="choices[{$i}]" value="1" />
                                 <label class="btn btn-default btn-xs" for="i-choice-{$i}" title="{_('Vote ifneedbe for')} {$slot->day|date_format:$date_format.txt_short} - {$moment}">
                                     (<span class="glyphicon glyphicon-ok"></span>)<span class="sr-only">{_('Ifneedbe')}</span>
                                 </label>
                             </li>
                             <li class="no">
-                                <input type="radio" id="n-choice{$i}" name="choice{$i}" value="0" checked/>
+                                <input type="radio" id="n-choice{$i}" name="choices[{$i}]" value="0" checked/>
                                 <label class="btn btn-default btn-xs" for="n-choice-{$i}" title="{_('Vote no for')} {$slot->day|date_format:$date_format.txt_short} - {$moment}">
                                     <span class="glyphicon glyphicon-ban-circle"></span><span class="sr-only">{_('No')}</span>
                                 </label>
@@ -203,7 +207,7 @@
                         {$i = $i+1}
                         {/foreach}
                     {/foreach}
-                    <td><button type="submit" class="btn btn-success btn-sm" name="add_vote" title="{_('Save the choices')}">{_('Save')}</button></td>
+                    <td><button type="submit" class="btn btn-success btn-sm" name="save" title="{_('Save the choices')}">{_('Save')}</button></td>
                 </tr>
             {/if}
 
-- 
GitLab