diff --git a/README.md b/README.md old mode 100644 new mode 100755 diff --git a/controllers/commands.php b/controllers/commands.php index c126324..c05f64d 100755 --- a/controllers/commands.php +++ b/controllers/commands.php @@ -45,9 +45,9 @@ * @param int... $ids : Les id des commandes à supprimer * @return boolean; */ - public function delete(...$ids) + public function delete($csrf, ...$ids) { - if (!internalTools::verifyCSRF()) + if (!internalTools::verifyCSRF($csrf)) { $_SESSION['errormessage'] = 'Jeton CSRF invalide !'; header('Location: ' . $this->generateUrl('commands', 'showAll')); @@ -86,14 +86,15 @@ /** * Cette fonction insert une nouvelle commande + * @param $csrf : Le jeton CSRF * @param string $_POST['name'] : Le nom de la commande * @param string $_POST['script'] : Le script a appeler * @param boolean $_POST['admin'] : Si la commande necessite les droits d'admin (par défaut non) * @return boolean; */ - public function create() + public function create($csrf) { - if (!internalTools::verifyCSRF()) + if (!internalTools::verifyCSRF($csrf)) { $_SESSION['errormessage'] = 'Jeton CSRF invalide !'; header('Location: ' . $this->generateUrl('commands', 'showAll')); @@ -123,12 +124,13 @@ /** * Cette fonction met à jour une commande + * @param $csrf : Le jeton CSRF * @param array $_POST['commands'] : Un tableau des commandes avec leur nouvelle valeurs * @return boolean; */ - public function update() + public function update($csrf) { - if (!internalTools::verifyCSRF()) + if (!internalTools::verifyCSRF($csrf)) { $_SESSION['errormessage'] = 'Jeton CSRF invalide !'; header('Location: ' . $this->generateUrl('commands', 'showAll')); diff --git a/controllers/contacts.php b/controllers/contacts.php index af4e772..c48fd0f 100755 --- a/controllers/contacts.php +++ b/controllers/contacts.php @@ -41,13 +41,14 @@ /** * Cette fonction supprimer une liste de contacts + * @param $csrf : Le jeton CSRF * @param int... $ids : Les id des commandes à supprimer * @return Boolean; */ - public function delete(...$ids) + public function delete($csrf, ...$ids) { //On vérifie que le jeton csrf est bon - if (!internalTools::verifyCSRF()) + if (!internalTools::verifyCSRF($csrf)) { $_SESSION['errormessage'] = 'Jeton CSRF invalide !'; header('Location: ' . $this->generateUrl('contacts', 'showAll')); @@ -86,13 +87,14 @@ /** * Cette fonction insert un nouveau contact + * @param $csrf : Le jeton CSRF * @param string $_POST['name'] : Le nom du contact * @param string $_POST['phone'] : Le numero de téléphone du contact */ - public function create() + public function create($csrf) { //On vérifie que le jeton csrf est bon - if (!internalTools::verifyCSRF()) + if (!internalTools::verifyCSRF($csrf)) { $_SESSION['errormessage'] = 'Jeton CSRF invalide !'; header('Location: ' . $this->generateUrl('contacts', 'showAll')); @@ -111,14 +113,14 @@ $nom = $_POST['name']; $phone = $_POST['phone']; - if ($phone = internalTools::parsePhone($phone)) + if (!$phone = internalTools::parsePhone($phone)) { $_SESSION['errormessage'] = 'Numéro de téléphone incorrect.'; header('Location: ' . $this->generateUrl('contacts', 'add')); return false; } - if (!$db->insertIntoTable('contacts', ['nom' => $nom, 'phone' => $phone])) + if (!$db->insertIntoTable('contacts', ['name' => $nom, 'number' => $phone])) { $_SESSION['errormessage'] = 'Impossible créer ce contact.'; header('Location: ' . $this->generateUrl('contacts', 'add')); @@ -134,13 +136,14 @@ /** * Cette fonction met à jour une liste de contacts + * @param $csrf : Le jeton CSRF * @param array $_POST['contacts'] : Un tableau des contacts avec leur nouvelle valeurs * @return boolean; */ - public function update() + public function update($csrf) { //On vérifie que le jeton csrf est bon - if (!internalTools::verifyCSRF()) + if (!internalTools::verifyCSRF($csrf)) { $_SESSION['errormessage'] = 'Jeton CSRF invalide !'; header('Location: ' . $this->generateUrl('contacts', 'showAll')); diff --git a/controllers/groups.php b/controllers/groups.php index ea1c8df..9d88fdf 100755 --- a/controllers/groups.php +++ b/controllers/groups.php @@ -39,13 +39,14 @@ /** * Cette fonction supprime une liste de groupes + * @param $csrf : Le jeton CSRF * @param int... $ids : Les id des groups à supprimer * @return void; */ - public function delete(...$ids) + public function delete($csrf, ...$ids) { //On vérifie que le jeton csrf est bon - if (!internalTools::verifyCSRF()) + if (!internalTools::verifyCSRF($csrf)) { $_SESSION['errormessage'] => 'Jeton CSRF invalide !'; header('Location: ' . $this->generateUrl('groups', 'showAll')); @@ -92,13 +93,14 @@ /** * Cette fonction insert un nouveau contact + * @param $csrf : Le jeton CSRF * @param string $_POST['name'] : Le nom du groupe * @param array $_POST['contacts'] : Les id des contacts à mettre dans le du groupe */ - public function create() + public function create($csrf) { //On vérifie que le jeton csrf est bon - if (!internalTools::verifyCSRF()) + if (!internalTools::verifyCSRF($csrf)) { $_SESSION['errormessage'] = 'Jeton CSRF invalide !'; header('Location: ' . $this->generateUrl('groups', 'showAll')); @@ -130,13 +132,14 @@ /** * Cette fonction met à jour une liste de groupes + * @param $csrf : Le jeton CSRF * @param array $_POST['groups'] : Un tableau des groups avec leur nouvelle valeurs * @return boolean; */ - public function update() + public function update($csrf) { //On vérifie que le jeton csrf est bon - if (!internalTools::verifyCSRF()) + if (!internalTools::verifyCSRF($csrf)) { $_SESSION['errormessage'] = 'Jeton CSRF invalide !'; header('Location: ' . $this->generateUrl('groups', 'showAll')); diff --git a/controllers/profile.php b/controllers/profile.php index 5345d20..80d23be 100755 --- a/controllers/profile.php +++ b/controllers/profile.php @@ -33,14 +33,15 @@ /** * Cette fonction change le mot de passe de l'utilisateur + * @param $csrf : Le jeton CSRF * @param string $_POST['password'] : Le nouveau mot de passe de l'utilisateur * @param string $_POST['verif_password'] : La vérification du nouveau mot de passe de l'utilisateur * @return void; */ - public function changePassword() + public function changePassword($csrf) { //On vérifie que le jeton csrf est bon - if (!internalTools::verifyCSRF()) + if (!internalTools::verifyCSRF($csrf)) { $_SESSION['errormessage'] = 'Jeton CSRF invalide !'; header('Location: ' . $this->generateUrl('profile', 'showAll'); @@ -74,14 +75,15 @@ /** * Cette fonction change l'email de l'utilisateur + * @param $csrf : Le jeton CSRF * @param string $_POST['email'] : Le nouvel email de l'utilisateur * @param string $_POST['verif_email'] : La vérification du nouvel email de l'utilisateur * @return void; */ - public function changeEmail() + public function changeEmail($csrf) { //On vérifie que le jeton csrf est bon - if (!internalTools::verifyCSRF()) + if (!internalTools::verifyCSRF($csrf)) { $_SESSION['errormessage'] = 'Jeton CSRF invalide !'; header('Location: ' . $this->generateUrl('profile', 'showAll'); @@ -127,13 +129,14 @@ /** * Cette fonction supprime l'utilisateur + * @param $csrf : Le jeton CSRF * @param string $_POST['delete_account'] : La vérification que l'on veux bien supprimer l'utilisateur * @return void; */ - public function delete() + public function delete($csrf) { //On vérifie que le jeton csrf est bon - if (!internalTools::verifyCSRF()) + if (!internalTools::verifyCSRF($csrf)) { $_SESSION['errormessage'] = 'Jeton CSRF invalide !'; header('Location: ' . $this->generateUrl('profile', 'showAll'); diff --git a/controllers/scheduleds.php b/controllers/scheduleds.php index 84795cb..ac01c0e 100755 --- a/controllers/scheduleds.php +++ b/controllers/scheduleds.php @@ -39,13 +39,14 @@ /** * Cette fonction supprime une liste de groupes + * @param $csrf : Le jeton CSRF * @param int... $ids : Les id des commandes à supprimer * @return boolean; */ - public function delete(...$ids) + public function delete($csrf, ...$ids) { //On vérifie que le jeton csrf est bon - if (!internalTools::verifyCSRF()) + if (!internalTools::verifyCSRF($csrf)) { $_SESSION['errormessage'] = 'Jeton CSRF invalide !'; header('Location: ' . $this->generateUrl('profile', 'showAll'); @@ -101,6 +102,7 @@ /** * Cette fonction insert un nouveau SMS programmé + * @param $csrf : Le jeton CSRF * @param optionnal boolean $api : Si vrai (faux par défaut), on retourne des réponses au lieu de rediriger * @param string $_POST['date'] : La date a la quelle de sms devra être envoyé * @param string $_POST['content'] : Le contenu du SMS @@ -109,12 +111,12 @@ * @param string $_POST['groups'] : Un tableau avec les ids des groupes auxquels envoyer le sms * @return boolean; */ - public function create($api = false) + public function create($csrf = '', $api = false) { if (!$api) { //On vérifie que le jeton csrf est bon - if (!internalTools::verifyCSRF()) + if (!internalTools::verifyCSRF($csrf)) { $_SESSION['errormessage'] = 'Jeton CSRF invalide !'; header('Location: ' . $this->generateUrl('profile', 'showAll'); @@ -228,12 +230,13 @@ /** * Cette fonction met à jour une liste de sms + * @param $csrf : Le jeton CSRF * @param array $_POST['scheduleds'] : Un tableau contenant les sms avec leurs nouvelles valeurs */ - public function update() + public function update($csrf) { //On vérifie que le jeton csrf est bon - if (!internalTools::verifyCSRF()) + if (!internalTools::verifyCSRF($csrf)) { $_SESSION['successmessage'] = 'Jeton CSRF invalide !'; header('Location: ' . $this->generateUrl('scheduleds', 'showAll'); diff --git a/controllers/users.php b/controllers/users.php index e4d2696..1210803 100755 --- a/controllers/users.php +++ b/controllers/users.php @@ -50,14 +50,15 @@ /** * Cette fonction insert un nouvel utilisateur + * @param $csrf : Le jeton CSRF * @param string $_POST['email'] : L'email de l'utilisateur * @param string $_POST['email_confirm'] : Confirmation de l'email de l'utilisateur * @param boolean $_POST['admin'] : Optionnel : Si l'utilisateur est admin. Par défaut non */ - public function create() + public function create($csrf) { //On vérifie que le jeton csrf est bon - if (!internalTools::verifyCSRF()) + if (!internalTools::verifyCSRF($csrf)) { $_SESSION['errormessage'] = 'Jeton CSRF invalide !'; header('Location: ' . $this->generateUrl('users', 'add')); @@ -122,13 +123,14 @@ /** * Cette fonction supprimer une liste d'utilisateur + * @param $csrf : Le jeton CSRF * @param int... $ids : Les id des commandes à supprimer * @return boolean; */ - public function delete(...$ids) + public function delete($csrf, ...$ids) { //On vérifie que le jeton csrf est bon - if (!internalTools::verifyCSRF()) + if (!internalTools::verifyCSRF($csrf)) { $_SESSION['errormessage'] = 'Jeton CSRF invalide !'; header('Location: ' . $this->generateUrl('users', 'showAll')); diff --git a/createDatabase.sql b/createDatabase.sql index 1763ae1..d1ca2a3 100755 --- a/createDatabase.sql +++ b/createDatabase.sql @@ -100,7 +100,7 @@ CREATE TABLE events ( id INT NOT NULL AUTO_INCREMENT, type VARCHAR(25) NOT NULL, - at DATETIME NOT NULL, + at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, text VARCHAR(255) NOT NULL, PRIMARY KEY (id) ); diff --git a/fonts/glyphicons-halflings-regular.woff2 b/fonts/glyphicons-halflings-regular.woff2 old mode 100644 new mode 100755 diff --git a/mvc/Controller.php b/mvc/Controller.php index 0bdc641..8dd6ef5 100755 --- a/mvc/Controller.php +++ b/mvc/Controller.php @@ -53,7 +53,7 @@ $chemin_template = PWD_TEMPLATES . $template . '.php'; if(file_exists($chemin_template)) { - require_once($chemin_template); + require($chemin_template); unset($chemin_template); return true; } @@ -65,19 +65,31 @@ * Cette fonction permet de générer une adresse URL interne au site * @param string $controller : Nom du controleur à employer (par défaut vide) * @param string $method : Nom de la méthode à employer (par défaut vide) - * @param string $params : Tableau des paramètres $_GET à employer au format 'nom' => valeur (par défaut array()) + * @param string $params : Tableau des paramètres à passer à la fonction, sous forme de tableau, sans clefs + * @param string $getParams : Tableau des paramètres $_GET à employer au format 'nom' => valeur (par défaut array()) * @return string, Url générée */ - protected function generateUrl($controller = '', $method = '', $params = array()) + protected function generateUrl($controller = '', $method = '', $params = array(), $getParams = array()) { $url = HTTP_PWD; $url .= ($controller ? $controller . '/' : ''); $url .= ($method ? $method . '/' : ''); - foreach ($params as $clef => $valeur) + + //On ajoute les paramètres framework + foreach ($params as $valeur) { - $url .= $clef . '_' . rawurlencode($valeur) . '/'; + $url .= rawurlencode($valeur) . '/'; } + //On calcul puis ajoute les paramètres get + $paramsToJoins = array(); + foreach ($getParams as $clef => $valeur) + { + $paramsToJoins[] = $clef . '=' . rawurlencode($valeur); + } + + $url .= count($getParams) ? '?' . implode('&', $paramsToJoins) : ''; + return $url; } diff --git a/mvc/Model.php b/mvc/Model.php index 1de6cea..428f203 100755 --- a/mvc/Model.php +++ b/mvc/Model.php @@ -29,20 +29,164 @@ $this->bdd = $bdd; } + /* + Fonctions relatives aux informations de la base + */ + + /** + * Cette fonction vérifie si une table existe + * @param string $table : Nom de la table + * @return mixed : Vrai si la table existe, faux sinon + */ + public function tableExist($table) + { + $tables = $this->getAllTables(); + return in_array($table, $tables); + } + + /** + * Cette fonction vérifie si un champs existe dans une table + * @param string $field : Nom du champ + * @param string $table : Nom de la table + * @return mixed : Vrai si le champs existe, faux, si le champs ou la table n'existe pas + */ + public function fieldExist($field, $table) + { + $fields = $this->getColumnsForTable($table); + $fields = $fields ? explode(', ', $fields) : array(); + return in_array($field, $fields); + } + + /** + * Cette requete retourne le dernier id inséré + * return int : le dernier id inséré + */ + public function lastId() + { + return $this->bdd->lastInsertId(); + } + + /** + * Cette fonction permet de retourner toutes les tables de la base + * @return array : La liste des tables + */ + public function getAllTables() + { + $query = 'SHOW TABLES'; + $tables = $this->runQuery($query); + $tablesNames = array(); + + foreach ($tables as $table) + { + $tablesNames[] = array_values($table)[0]; + } + + return $tablesNames; + } + + /** + * Cette fonction permet de récupérer la liste de toutes les colonnes d'une table de façon propre, appelable via MySQL. Cela permet de faire des requetes plus optimisée qu'avec "*" + * @param string $table : Nom de la table pour laquelle on veux les colonnes + * @param string $prefix : Le prefix que l'on veux devant les champs, utile pour les requetes avec jointures. Par défaut null => pas de prefix. A noter, en cas d'utilisation de prefix, les champs aurons un alias de la forme $prefix_$nom_champ + * @return boolean string : Tous les noms des colonnes liées par des ", ". Ex : 'id, nom, prenom". Si la table n'existe pas, on retourne false. + */ + public function getColumnsForTable($table, $prefix = null) + { + if ($this->tableExist($table)) + { + $query = 'SHOW COLUMNS FROM ' . $table; + + $datas = array(); + + $fields = $this->runQuery($query, $datas, self::FETCHALL); + $fieldsName = array(); + foreach ($fields as $field) + { + $fieldsName[] = $prefix ? $prefix . '.' . $field['Field'] . ' AS ' . $prefix . '_' . $field['Field'] : $field['Field']; + } + + return implode(', ', $fieldsName); + } + + return false; + } + + /** + * Cette fonction décrit une table et retourne un tableau sur cette description + * @param string $table : Le nom de la table a analyser + * @return mixed : Si la table existe un tableau la décrivant, sinon false + */ + public function describeTable($table) + { + if (!$this->tableExist($table)) + { + return false; + } + + //On recupere tous les champs pour pouvoir apres les analyser + $query = 'DESCRIBE ' . $table; + $fields = $this->runQuery($query); + + $return = array(); + foreach ($fields as $field) + { + $fieldInfo = array(); + $fieldInfo['NULL'] = $field['Null'] == 'NO' ? false : true; + $fieldInfo['AUTO_INCREMENT'] = $field['Extra'] == 'auto_increment' ? true : false; + $fieldInfo['PRIMARY'] = $field['Key'] == 'PRI' ? true : false; + $fieldInfo['UNIQUE'] = $field['Key'] == 'UNI' ? true : false; + $fieldInfo['TYPE'] = mb_convert_case(preg_replace('#[^a-z]#ui', '', $field['Type']), MB_CASE_UPPER); + $fieldInfo['SIZE'] = filter_var($field['Type'], FILTER_SANITIZE_NUMBER_INT); + $fieldInfo['HAS_DEFAULT'] = $field['Default'] !== NULL ? true : false; + $fieldInfo['DEFAULT'] = $field['Default']; + $return[$field['Field']] = $fieldInfo; + } + + return $return; + } + + /** + * Cette fonction permet de compter le nombre de ligne d'une table + * @param string $table : Le nom de la table à compter + * @return mixed : Le nombre de ligne dans la table ou false si la table n'existe pas + */ + public function countTable ($table) + { + if (!$this->tableExist($table)) + { + return false; + } + + $query = "SELECT COUNT(*) as nb_lignes FROM " . $table; + + $return = $this->runQuery($query, array(), self::FETCH); + return $return['nb_lignes']; + } + + /* + Fonctions d'execution des requetes ou de génération + */ + /** * Cette fonction joue une requete depuis une requete et un tableau d'argument * @param string $query : Requete à jouer * @param array $datas : Les données pour la requete. Si non fourni, vide par défaut. * @param const $return_type : Type de retour à utiliser. (Voir les constantes de la classe Model ici présente). Par défaut FETCHALL * @param const $fetch_mode : Le type de récupération a effectuer. Par défaut FETCH_ASSOC + * @param boolean $debug : Par défaut à faux, si vrai retourne les infos de débug de la requete * @return mixed : Dépend du type spécifié dans $return_type */ - public function runQuery($query, $datas = array(), $return_type = self::FETCHALL, $fetch_mode = PDO::FETCH_ASSOC) + public function runQuery($query, $datas = array(), $return_type = self::FETCHALL, $fetch_mode = PDO::FETCH_ASSOC, $debug = false) { $req = $this->bdd->prepare($query); $req->setFetchMode($return_type); $req->execute($datas); + if ($debug) + { + return $req->errorInfo(); + } + switch ($return_type) { case self::NO : @@ -68,62 +212,6 @@ return $return; } - /** - * Cette fonction vérifie si une table existe - * @param string $table : Nom de la table - * @return mixed : Vrai si la table existe, faux sinon - */ - public function tableExist($table) - { - $query = ' - SHOW TABLES LIKE :table - '; - - $query_datas = array( - 'table' => $table - ); - - $req = $this->bdd->prepare($query); - $req->execute($query_datas); - $result = $req->fetch(); - if(count($result)) - { - return true; - } - - return false; - } - - /** - * Cette fonction vérifie si un champs existe dans une table - * @param string $field : Nom du champ - * @param string $table : Nom de la table - * @return mixed : Vrai si le champs existe, faux, si le champs ou la table n'existe pas - */ - public function fieldExist($field, $table) - { - if($this->tableExist($table)) - { - $query = ' - SHOW COLUMNS FROM ' . $table . ' LIKE :field - '; - - $query_datas = array( - 'field' => $field - ); - - $req = $this->bdd->prepare($query); - $req->execute($query_datas); - $result = $req->fetch(); - if(count($result)) - { - return true; - } - } - - return false; - } - /** * Cette fonction permet de récupérer les éléments necessaires à une requete 'IN' depuis un tableau php * @param string $values : Tableau PHP des valeurs @@ -150,91 +238,242 @@ return $return; } - /** - * Cette requete retourne le dernier id inséré - * return int : le dernier id inséré - */ - public function lastId() - { - return $this->bdd->lastInsertId(); - } + + /* + Fonctions de manipulations basiques des données + */ /** - * Cette fonction permet de récupérer la liste de toutes les colonnes d'une table de façon propre, appelable via MySQL. Cela permet de faire des requetes plus optimisée qu'avec "*" - * @param string $table : Nom de la table pour laquelle on veux les colonnes - * @return boolean string : Tous les noms des colonnes liées par des ", ". Ex : 'id, nom, prenom". Si la table n'existe pas, on retourne false. - */ - public function getColumnsForTable($table) - { - if ($this->tableExist($table)) - { - $query = 'SHOW COLUMNS FROM ' . $table; - - $datas = array(); - - $fields = $this->runQuery($query, $datas, self::FETCHALL); - $fieldsName = array(); - foreach ($fields as $field) - { - $fieldsName[] = $field['Field']; - } - - return implode(', ', $fieldsName); - } - - return false; - } - - /** - * Cette fonction permet de récupérer une table complète, éventuellement en la triant par une colonne, éventuellement en limitant le nombre de résultat, ou en sautant certains (notamment pour de la pagination) - * @param string $table : Le nom de la table a récupérer + * Cette fonction permet de récupérer des lignes en fonction de restrictions + * @param string $table : Le nom de la table dans laquelle on veux recuperer la ligne + * @param array $restrictions : Les restrictions que l'on veux appliquer * @param string $order_by : Le nom de la colonne par laquelle on veux trier les résultats. Si non fourni, tri automatique * @param string $desc : L'ordre de tri (asc ou desc). Si non défini, ordre par défaut (ASC) * @param string $limit : Le nombre maximum de résultats à récupérer (par défaut pas le limite) * @param string $offset : Le nombre de résultats à ignorer (par défaut pas de résultats ignorés) - * @return array : Tableau avec dans chaque case une ligne de la base + * @return mixed : False en cas d'erreur, sinon les lignes retournées */ - public function getAll($table, $order_by = '', $desc = false, $limit = false, $offset = false) + public function getFromTableWhere($table, $restrictions = array(), $order_by = '', $desc = false, $limit = false, $offset = false) { - if ($this->tableExist($table)) + $restrictions = !is_array($restrictions) ? array() : $restrictions; + + $fields = $this->describeTable($table); + if (!$fields) { - $query = "SELECT " . $this->getColumnsForTable($table) . " FROM " . $table; - - if ($order_by) - { - if ($this->fieldExist($order_by, $table)) - { - $query .= ' ORDER BY '. $order_by; - if ($desc) - { - $query .= ' DESC'; - } - } - } - - if ($limit !== false) - { - $query .= ' LIMIT :limit'; - if ($offset !== false) - { - $query .= ' OFFSET :offset'; - } - } - - $req = $this->bdd->prepare($query); - - if ($limit !== false) - { - $req->bindParam(':limit', $limit, PDO::PARAM_INT); - if ($offset !== false) - { - $req->bindParam(':offset', $offset, PDO::PARAM_INT); - } - } - - $req->setFetchMode(PDO::FETCH_ASSOC); - $req->execute(); - return $req->fetchAll(); + return false; } + + //On gère les restrictions + $wheres = array(); + $params = array(); + + foreach ($restrictions as $label => $value) + { + //Si le champs pour la restriction n'existe pas on retourne false + if (!array_key_exists($label, $fields)) + { + return false; + } + + //On ajoute la restriction au WHERE + $params['where_' . $label] = $value; + $wheres[] = $label . ' = :where_' . $label . ' '; + } + + $query = "SELECT " . $this->getColumnsForTable($table) . " FROM " . $table . " WHERE 1 " . (count($wheres) ? 'AND ' : '') . implode('AND ', $wheres); + + if ($order_by) + { + if ($this->fieldExist($order_by, $table)) + { + $query .= ' ORDER BY '. $order_by; + if ($desc) + { + $query .= ' DESC'; + } + } + } + + if ($limit !== false) + { + $query .= ' LIMIT :limit'; + if ($offset !== false) + { + $query .= ' OFFSET :offset'; + } + } + + $req = $this->bdd->prepare($query); + + if ($limit !== false) + { + $req->bindParam(':limit', $limit, PDO::PARAM_INT); + if ($offset !== false) + { + $req->bindParam(':offset', $offset, PDO::PARAM_INT); + } + } + + //On associe les paramètres + foreach ($params as $label => &$param) + { + $req->bindParam(':' . $label, $param); + } + + $req->setFetchMode(PDO::FETCH_ASSOC); + $req->execute(); + return $req->fetchAll(); } + + /** + * Cette fonction permet de modifier les données d'une table pour un la clef primaire + * @param string $table : Le nom de la table dans laquelle on veux insérer des données + * @param string $primary : La clef primaire qui sert à identifier la ligne a modifier + * @param array $datas : Les données à insérer au format "champ" => "valeur" + * @param array $restrictions : Un tableau des restrictions à appliquer sous forme "champ" => "valeur". Par défaut un tableau vide + * @return mixed : False en cas d'erreur, sinon le nombre de lignes modifiées + */ + public function updateTableWhere ($table, $datas, $restrictions = array()) + { + $fields = $this->describeTable($table); + if (!$fields) + { + return false; + } + + $params = array(); + $sets = array(); + + //On gère les set + foreach ($datas as $label => $value) + { + //Si le champs pour la nouvelle valeur n'existe pas on retourne false + if (!array_key_exists($label, $fields)) + { + return false; + } + + //Si le champs est Nullable est qu'on à reçu une chaine vide, on passe à null plutot qu'à chaine vide + if ($fields[$label]['NULL'] && $value === '') + { + $value = null; + } + + $params['set_' . $label] = $value; + $sets[] = $label . ' = :set_' . $label . ' '; + } + + //On gère les restrictions + $wheres = array(); + foreach ($restrictions as $label => $value) + { + //Si le champs pour la restriction n'existe pas on retourne false + if (!array_key_exists($label, $fields)) + { + return false; + } + + //On ajoute la restriction au WHERE + $params['where_' . $label] = $value; + $wheres[] = $label . ' = :where_' . $label . ' '; + } + + //On fabrique la requete + $query = "UPDATE " . $table . " SET " . implode(', ', $sets) . " WHERE 1 AND " . implode('AND ', $wheres); + + //On retourne le nombre de lignes insérées + return $this->runQuery($query, $params, self::ROWCOUNT); + } + + /** + * Cette fonction permet de supprimer des lignes d'une table en fonctions de restrictions + * @param string $table : Le nom de la table dans laquelle on veux supprimer la ligne + * @param array $restrictions : Les restrictions pour la suppression sous la forme "label" => "valeur" + * @return mixed : False en cas d'erreur, sinon le nombre de lignes supprimées + */ + public function deleteFromTableWhere($table, $restrictions = array()) + { + + $fields = $this->describeTable($table); + if (!$fields) + { + return false; + } + + //On gère les restrictions + $wheres = array(); + $params = array(); + + foreach ($restrictions as $label => $value) + { + //Si le champs pour la restriction n'existe pas on retourne false + if (!array_key_exists($label, $fields)) + { + return false; + } + + //On ajoute la restriction au WHERE + $params['where_' . $label] = $value; + $wheres[] = $label . ' = :where_' . $label . ' '; + } + + $query = "DELETE FROM " . $table . " WHERE 1 AND " . implode('AND ', $wheres); + + return $this->runQuery($query, $params, self::ROWCOUNT); + } + + /** + * Cette fonction permet d'insérer des données dans une table + * @param string $table : Le nom de la table dans laquelle on veux insérer des données + * @param array $datas : Les données à insérer + * @return mixed : False en cas d'erreur, et le nombre de lignes insérées sinon + */ + public function insertIntoTable($table, $datas) + { + $fields = $this->describeTable($table); + if (!$fields) + { + return false; + } + + $params = array(); + $fieldNames = array(); + + //On s'assure davoir toutes les données, on evite les auto increment, on casse en cas de donnée absente + foreach ($fields as $nom => $field) + { + if ($field['AUTO_INCREMENT']) + { + continue; + } + + //Si il manque un champs qui peux être NULL ou qu'il est rempli avec une chaine vide ou null, on passe au suivant + if ((!isset($datas[$nom]) || $datas[$nom] === NULL || $datas[$nom] === '') && $field['NULL']) + { + continue; + } + + //Si il manque un champs qui a une valeur par défaut + if (!isset($datas[$nom]) && $field['HAS_DEFAULT']) + { + continue; + } + + //Si il nous manque un champs + if (!isset($datas[$nom])) + { + return false; + } + + $params[$nom] = $datas[$nom]; + $fieldNames[] = $nom; + } + + //On fabrique la requete + $query = "INSERT INTO " . $table . "(" . implode(', ', $fieldNames) . ") VALUES(:" . implode(', :', $fieldNames) . ")"; + + //On retourne le nombre de lignes insérées + return $this->runQuery($query, $params, self::ROWCOUNT); + } + } diff --git a/mvc/Router.php b/mvc/Router.php index a9d960e..9016e88 100755 --- a/mvc/Router.php +++ b/mvc/Router.php @@ -68,6 +68,16 @@ $this->params = $value; } + /** + * Cette méthode retourne la page 404 par défaut + */ + public function return404 () + { + http_response_code(404); + include(PWD . 'mvc/404.php'); + die(); + } + /** * Retourne une route avec seulement l'url parsée comme il faut * @param string $route : La route à analyser @@ -77,12 +87,12 @@ { $directory_to_remove = strstr(preg_replace('#http(s)?://#', '', HTTP_PWD), '/'); //On retire la partie protocole, et l'adresse du serveur de la partie de la route à ignorer $route = mb_strcut($route, mb_strlen($directory_to_remove)); //On retire la partie à ignorer de la route - - $route = preg_split('#[/?]#', $route); //On explose la route + $route = explode('?', $route)[0]; //on ne garde que ce qui précède les arguments + $route = preg_split('#[/]#', $route); //On explose la route foreach($route as $key => $val) //On garde seulement les repertoires non vides { - if(empty($val)) + if(empty($val) && $val !== 0 && $val !== '0') { unset($route[$key]); } @@ -100,17 +110,21 @@ public function parseController($route) { $route = $this->parseRoute($route); //On récupère la route parsé - //On lie le bon controlleur - if (empty($route[0]) || !file_exists(PWD_CONTROLLER . $route[0] . '.php') || mb_strpos($route[0], 'internal') !== false) //Si on a pas de premier parametre, ou qu'il ne correspond à aucun controlleur + + //Si pas de controlleur, on prend celui par défaut + if (empty($route[0])) { - $controllerName = DEFAULT_CONTROLLER; //On défini le nom du controlleur par défaut + return DEFAULT_CONTROLLER; } - else //Sinon, si tout est bon + + //Sinon, si le controlleur n'existe pas ou est internal, on retourne une 404 + if (!file_exists(PWD_CONTROLLER . $route[0] . '.php') || mb_strpos($route[0], 'internal') !== false) { - $controllerName = $route[0]; //On défini le nom du controlleur + return $this->return404(); } - - return $controllerName; + + //On retourne le controlleur adapté + return $route[0]; } /** @@ -120,20 +134,71 @@ */ public function parseMethod($route) { + //On instancie le controlleur $controllerName = $this->parseController($route); - require_once(PWD_CONTROLLER . $controllerName . '.php'); //On inclus le controlleur $controller = new $controllerName(); - $route = $this->parseRoute($route); //On récupère la route parsé - //On lie la bonne méthode - if (empty($route[1]) || !method_exists($controller, $route[1])) //Si on a pas de second parametre, ou qu'il ne correspond à aucune méthode du controlleur + $prefixMethod = ''; + + //On recupère les paramètres dans l'url pour les utiliser un peu plus tard + $params = $this->parseParams($route); + + + //On vérifie si le controlleur est un controlleur API, si c'est le cas on le refais avec cette fois la bonne methode + if ($controller instanceof ApiController) { - $method = DEFAULT_METHOD; //On prend la méthode par défaut - } - else //Sinon, si tout est bon - { - $method = $route[1]; //On défini la méthode appelée + //On va choisir le type à employer + $method = $_SERVER['REQUEST_METHOD']; + switch (mb_convert_case($method, MB_CASE_LOWER)) + { + case 'delete' : + $prefixMethod = 'delete'; + break; + case 'patch' : + $prefixMethod = 'patch'; + break; + case 'post' : + $prefixMethod = 'post'; + break; + case 'put' : + $prefixMethod = 'put'; + break; + default : + $prefixMethod = 'get'; + } } + $route = $this->parseRoute($route); //On récupère la route parsé + + //On regarde quelle route il nous faut et on evite les routes qui commencent par '_', qui sont maintenant considérées comme privées + if (empty($route[1])) + { + $method = DEFAULT_METHOD; + } + else + { + $method = $route[1]; + } + + if ($prefixMethod) + { + $method = $prefixMethod . mb_convert_case($method, MB_CASE_TITLE); + } + + //Si la méthode à appeler n'existe pas ou si la route commencent par '_', signe qu'elle est un outils non accessibles + if (!method_exists($controller, $method) || mb_substr($method, 0, 1) == '_') + { + return $this->return404(); + } + + //On instancie la classe reflection de PHP sur la méthode que l'on veux analyser pour l'objet controller + $methodAnalyser = new ReflectionMethod($controller, $method); + + //Si la méthode à appeler demande plus de paramètres que fournis on retourne une 404 + if ($methodAnalyser->getNumberOfRequiredParameters() > count($params)) + { + return $this->return404(); + } + return $method; } @@ -146,48 +211,83 @@ { $route = $this->parseRoute($route); //On récupère la route parsé $params = array(); - $already_use_params = array(); - //On transforme les paramètres $_GET passés par l'url au format clef_value. Ex : prenom_pierre-lin = $_GET['prenom'] => 'pierre-lin' + + //On transforme les paramètres $_GET passés par l'url au format clef-value. Ex : prenom-pierre-lin = $_GET['prenom'] => 'pierre-lin' if (count($route) > 2) //Si on a plus de deux paramètres qui ont été passé { unset($route[0], $route[1]); //On supprime le controlleur, et la route, des paramètres, il ne reste que les parametres a passer en GET - foreach($route as $param) //On passe sur chaque paramètre a transformer en GET + foreach ($route as $param) { - $param = explode('_', $param, 2); //On récupère le paramètre, via la délimiteur '_', en s'arretant au premier - - //Si on a déjà utilisé cette variable GET - if (in_array($param[0], $already_use_params)) - { - if (isset($params[$param[0]])) - { - $tmp_value = $params[$param[0]]; - $params[$param[0]] = array($tmp_value); - unset($tmp_value); - } - - $params[$param[0]][] = (isset($param[1])) ? rawurldecode($param[1]) : NULL; - } - else - { - $params[$param[0]] = (isset($param[1])) ? rawurldecode($param[1]) : NULL; //On définit la propriétée GET correspondante - $already_use_params[] = $param[0]; - } + $params[] = rawurldecode($param); } } - return $params; } + /** + * Cette fonction permet de vérifier si le cache est activé pour une route, et si oui quel fichier utiliser + * @param string $route : Route à analyser + * @return mixed : Si pas de cache, faux. Sinon un tableau avec deux clefs, "state" => statut du nom de fichier retourné (true, le fichier doit être utilisé, false, le fichier doit être créé), "file" => Le nom du fcihier de cache + */ + public function verifyCache($route) + { + //On récupère le nom du controller et de la méthode + $controllerName = $this->parseController($route); + $methodName = $this->parseMethod($route); + $params = $this->parseParams($route); + + $controller = new $controllerName(); + + //Si on ne doit pas activer le cache ou si on na pas de cache pour ce fichier + if (!ACTIVATING_CACHE || !property_exists($controller, 'cache_' . $methodName)) + { + return false; + } + + //Si on a du cache, on va déterminer le nom approprié + //Format de nom = + $hashName = md5($controllerName . $methodName); + $hashDatas = md5(json_encode($_GET) . json_encode($_POST) . json_encode($params)); + $fileName = $hashName . $hashDatas; + + //Si il n'existe pas de fichier de cache pour ce fichier + if (!file_exists(PWD_CACHE . $fileName)) + { + return array('state' => false, 'file' => $fileName); + } + + //Sinon, si le fichier de cache existe + $fileLastChange = filemtime(PWD_CACHE . $fileName); + + //On calcul la date de mise à jour valide la plus ancienne possible + $now = new DateTime(); + $propertyName = 'cache_' . $methodName; + $propertyValue = $controller->$propertyName; + $now->sub(new DateInterval('PT' . $propertyValue . 'M')); + $maxDate = $now->format('U'); + + //Si le fichier de cache est trop vieux + if ($fileLastChange < $maxDate) + { + return array('state' => false, 'file' => $fileName); + } + + //Sinon, on retourne le fichier de cache en disant de l'utiliser + return array('state' => true, 'file' => $fileName); + } + /** * Cette fonction permet de charger la page adaptée a partir d'une url + * Elle gère également le cache * @param string $route : Route à analyser pour charger une page * @return void */ public function loadRoute($route) { - $_GET = array_merge($_GET, $this->parseParams($route)); //On fusionne les paramètres GET et ceux passés dans la route du controller + $params = $this->parseParams($route); //On récupère les paramètres à passer à la fonction $controllerName = $this->parseController($route); //On récupère le nom du controleur à appeler $controller = new $controllerName(); //On créer le controleur + $beforeMethodName = DEFAULT_BEFORE; //On défini le nom de la fonction before if (method_exists($controller, $beforeMethodName)) //Si une fonction before existe pour ce controller, on l'appel { @@ -195,7 +295,27 @@ } $methodName = $this->parseMethod($route); //On récupère le nom de la méthode - $controller->$methodName(); //On appel la méthode - } + $verifyCache = $this->verifyCache($route); + //Si on ne doit pas utiliser de cache + if ($verifyCache === false) + { + call_user_func_array(array($controller, $methodName), $params); //On appel la methode, en lui passant les arguments necessaires + return null; + } + + //Si on doit utiliser un cache avec un nouveau fichier + if ($verifyCache['state'] == false) + { + //On créer le fichier avec le contenu adapté + ob_start(); + call_user_func_array(array($controller, $methodName), $params); //On appel la methode, en lui passant les arguments necessaires + $content = ob_get_contents(); + file_put_contents(PWD_CACHE . $verifyCache['file'], $content); + ob_end_clean(); + } + + //On utilise le fichier de cache + readfile(PWD_CACHE . $verifyCache['file']); + } } diff --git a/mvc/autoload.php b/mvc/autoload.php index c9d4938..b90fd5d 100755 --- a/mvc/autoload.php +++ b/mvc/autoload.php @@ -9,6 +9,8 @@ */ function autoloader($class) { + $class = str_replace('\\', '/', $class); #Gestion des namespaces + if (file_exists(PWD_CONTROLLER . $class . '.php')) { require_once(PWD_CONTROLLER . $class . '.php'); @@ -17,6 +19,10 @@ { require_once(PWD_MODEL . $class . '.php'); } + else if (file_exists(PWD . 'mvc/modules/' . $class . '.php')) + { + require_once(PWD . 'mvc/modules/' . $class . '.php'); + } } spl_autoload_register('autoloader'); diff --git a/mvc/conn_bdd.php b/mvc/conn_bdd.php index 6b2d06a..0bd8b1b 100755 --- a/mvc/conn_bdd.php +++ b/mvc/conn_bdd.php @@ -1,19 +1,19 @@ - TRUE)); - $bdd->exec("SET CHARACTER SET utf8"); - } - catch(Exception $e) - { - // En cas d'erreur, on affiche un message et on arrête tout - die('Erreur : '.$e->getMessage()); - } -?> + TRUE)); + $bdd->exec("SET CHARACTER SET utf8"); + } + catch(Exception $e) + { + // En cas d'erreur, on affiche un message et on arrête tout + die('Erreur : '.$e->getMessage()); + } +?> diff --git a/mvc/constants.php b/mvc/constants.php index 040a134..af5259b 100755 --- a/mvc/constants.php +++ b/mvc/constants.php @@ -31,3 +31,18 @@ //Réglages des logs define('LOG_ACTIVATED', 1); //On active les logs + + //Réglages du cache + define('ACTIVATING_CACHE', false); //On desactive le cache + + //Réglages divers + define('WEBSITE_TITLE', 'RaspiSMS'); //Le titre du site + define('WEBSITE_DESCRIPTION', ''); //Description du site + define('WEBSITE_KEYWORDS', ''); //Mots clefs du site + define('WEBSITE_AUTHOR', 'Raspbian-France'); //Auteur du site + + //Réglages des identifiants de base de données + define('DATABASE_HOST', 'localhost'); //Hote de la bdd + define('DATABASE_NAME', 'raspisms'); //Nom de la bdd + define('DATABASE_USER', 'root'); //Utilisateur de la bdd + define('DATABASE_PASSWORD', 'ajani7725'); //Password de l'utilisateur diff --git a/mvc/pingback.php b/mvc/pingback.php deleted file mode 100755 index 37d1ee4..0000000 --- a/mvc/pingback.php +++ /dev/null @@ -1,23 +0,0 @@ - - - Ajout d'une nouvelle commande
-
+
diff --git a/templates/addContact.php b/templates/addContact.php index b6ca384..f2509fe 100755 --- a/templates/addContact.php +++ b/templates/addContact.php @@ -37,7 +37,7 @@

Ajout d'un contact

- +
diff --git a/templates/addGroup.php b/templates/addGroup.php index a406e25..421b796 100755 --- a/templates/addGroup.php +++ b/templates/addGroup.php @@ -37,7 +37,7 @@

Ajout d'un groupe

- +
diff --git a/templates/addScheduled.php b/templates/addScheduled.php index 71c94b1..116956f 100755 --- a/templates/addScheduled.php +++ b/templates/addScheduled.php @@ -37,7 +37,7 @@

Ajout d'un SMS programmé

- +
diff --git a/templates/addUser.php b/templates/addUser.php index 9e84764..d114266 100755 --- a/templates/addUser.php +++ b/templates/addUser.php @@ -37,7 +37,7 @@

Ajout d'un utilisateur

- +
diff --git a/templates/commands.php b/templates/commands.php index e535445..28838d4 100755 --- a/templates/commands.php +++ b/templates/commands.php @@ -72,8 +72,8 @@
diff --git a/templates/contacts.php b/templates/contacts.php index 53aa494..6ab076f 100755 --- a/templates/contacts.php +++ b/templates/contacts.php @@ -70,8 +70,8 @@
diff --git a/templates/editCommands.php b/templates/editCommands.php index 91bf0a1..0e04e74 100755 --- a/templates/editCommands.php +++ b/templates/editCommands.php @@ -37,7 +37,7 @@

Modification de commandes

- + Modification de contacts
- + Modification de groupes
- + Modification des SMS programmés
- + Action pour la sélection
diff --git a/templates/login.php b/templates/login.php index 085c64b..e447708 100755 --- a/templates/login.php +++ b/templates/login.php @@ -28,5 +28,4 @@
footer(); diff --git a/templates/profile.php b/templates/profile.php index 06b1e24..4052b8a 100755 --- a/templates/profile.php +++ b/templates/profile.php @@ -49,7 +49,7 @@

Modifier mot de passe

- +
@@ -71,7 +71,7 @@

Modifier e-mail

- +
@@ -91,7 +91,7 @@

Supprimer ce compte

- +
diff --git a/templates/users.php b/templates/users.php index 1758484..d9673b8 100755 --- a/templates/users.php +++ b/templates/users.php @@ -70,7 +70,7 @@