* @license GPL v3 * @license CECILL v2 * @version $Id$ * @copyright © 2011, Tela-Botanica */ class InventoryKeyWordList extends Cel { protected $suffixe = ''; protected $suffixe_table = null; protected $suffixe_champ = null; private function setChampsEtTablePourSuffixe($suffixe) { $this->suffixe = $suffixe; switch($suffixe) { case 'obs': $this->suffixe_table = '_obs'; $this->suffixe_champ = '_obs'; break; case 'images': $this->suffixe_table = '_images'; $this->suffixe_champ = '_image'; break; } } public function getElement($uid) { // Controle detournement utilisateur $id_utilisateur = $uid[1] ; $this->controleUtilisateur($uid[1]); $this->setChampsEtTablePourSuffixe($uid[0]); $this->envoyerJson(self::getMotsClefs($uid[1], $uid[0])); return TRUE; // compat: pourquoi renvoyer true si vide ? } static function getMotsClefs($uid, $type) { if($type == 'obs') { return Cel::db()->requeter(sprintf( 'SELECT mot_cle, id_mot_cle_obs, ce_mot_cle_obs_parent'. ' FROM cel_mots_cles_obs'. ' WHERE id_utilisateur = %s'. ' ORDER BY niveau', Cel::db()->proteger($uid))); } if($type == 'images') { return Cel::db()->requeter(sprintf( 'SELECT mot_cle, id_mot_cle_image, ce_mot_cle_image_parent'. ' FROM cel_mots_cles_images'. ' WHERE id_utilisateur = %s'. ' ORDER BY niveau', Cel::db()->proteger($uid))); } /* pour extraire un mot-clef en particulier (bien que getMotsClefId() soit plus adapté: array_walk($ret, create_function('&$val, $k, $keyword', 'if($val["mot_cle"] != $keyword) $val = NULL;' . 'else $val = $val["id_mot_cle_obs"];'), 'XXX'); $obsKeywordIdToDetach = array_filter($ret); $obsKeywordIdToDetach = array_pop($obsKeywordIdToDetach); */ return array(); } static function getMotsClefId($uid, $type, $keyword) { if($type == 'obs') { $ret = Cel::db()->requeter(sprintf( 'SELECT mot_cle, id_mot_cle_obs, ce_mot_cle_obs_parent'. ' FROM cel_mots_cles_obs'. ' WHERE id_utilisateur = %s'. ' AND mot_cle = %s'. ' ORDER BY niveau', Cel::db()->proteger($uid), Cel::db()->proteger($keyword) )); } if($type == 'images') { $ret = Cel::db()->requeter(sprintf( 'SELECT mot_cle, id_mot_cle_image, ce_mot_cle_image_parent'. ' FROM cel_mots_cles_images'. ' WHERE id_utilisateur = %s'. ' AND mot_cle = %s'. ' ORDER BY niveau', Cel::db()->proteger($uid), Cel::db()->proteger($keyword) )); } return @$ret[0]['id_mot_cle_obs']; } public function updateElement($uid, $pairs) { $id_utilisateur = $uid[1]; $this->controleUtilisateur($uid[1]); $this->setChampsEtTablePourSuffixe($uid[0]); $id_mot_cle = $pairs['id']; $action = $pairs['action']; if ($action == 'modification') { $nouveau_nom = $pairs['motcle']; $nouvel_id_general = md5(mb_strtolower($nouveau_nom)); $requete = 'UPDATE cel_mots_cles'.$this->suffixe_table.' '. 'SET mot_cle = '.Cel::db()->proteger($nouveau_nom).' , '. ' md5 = '.Cel::db()->proteger($nouvel_id_general).' '. 'WHERE id_mot_cle'.$this->suffixe_champ.' = '.Cel::db()->proteger($id_mot_cle).' '. ' AND id_utilisateur = '.Cel::db()->proteger($id_utilisateur) ; $reussite = Cel::db()->executer($requete); if ($reussite !== false) { echo 'OK'; return true; } else { return false; } } else if ($action == 'deplacement') { self::commencerTransaction(); $transaction_reussie_1 = true; $id_pere = $pairs['parent']; $bornes = $this->calculerBornesEtNiveau($id_mot_cle, $id_utilisateur); $bg = $bornes['bg']; $bd = $bornes['bd']; $niveau = $bornes['niveau']; // on inverse l'intervalle de l'élément déplacé et du sous arbre $transaction_reussie_2 = $this->exclureIntervalle($bg, $bd, $id_utilisateur); $bg_negative = $bg - $bd - 1; $bd_negative = $bd - $bd - 1; // on recalcule les intervalles de l'arbre privé de ce sous arbre $transaction_reussie_3 = $this->decalerBornesMoinsIntervalle($bg, $bd, $id_utilisateur); $bornes_pere = $this->calculerBornesEtNiveau($id_pere, $id_utilisateur); $bg_pere = $bornes_pere['bg']; $bd_pere = $bornes_pere['bd']; $niveau_pere = $bornes_pere['niveau']; $decalage = $bd - $bg + 1; // on decale les bornes droite du pere pour préparer l'insertion $transaction_reussie_4 = $this->decalerBornesPlusIntervalle($bd_pere, $decalage, $id_utilisateur); $nouvelle_bd = $bd_pere + $decalage; $modif_niveau = $niveau_pere - $niveau + 1; $transaction_reussie_5 = $this->inclureIntervalle($bg_negative, $bd_negative, $nouvelle_bd, $modif_niveau, $id_utilisateur); $transaction_reussie_6 = $this->changerPere($id_mot_cle, $id_pere, $id_utilisateur); if ($transaction_reussie_1 !== false && $transaction_reussie_2 !== false && $transaction_reussie_3 !== false && $transaction_reussie_4 !== false && $transaction_reussie_5 !== false && $transaction_reussie_6 !== false) { self::completerTransaction(); return true; } else { self::annulerTransaction(); return false; } } return true; } public function createElement($pairs) { // Controle detournement utilisateur $this->controleUtilisateur($pairs['identifiant']); $this->setChampsEtTablePourSuffixe($pairs['mode']); $id_utilisateur = $pairs['identifiant']; $mot_cle = $pairs['motcle']; // TODO supprimer accents $id_mot_cle_general = md5(mb_strtolower($mot_cle)); $id_mot_cle = $pairs['id']; $id_parent = $pairs['parent']; $this->ajouterMotCleRacine($id_utilisateur); $bornes = $this->calculerBornesEtNiveau($id_parent, $id_utilisateur); $borne_pere = $bornes['bd']; $niveau = $bornes['niveau'] + 1; $bg = $bornes['bd']; $bd = $bg + 1; if(!$borne_pere) return false; self::commencerTransaction(); $transaction_reussie_1 = $this->decalerBornesPlusDeux($borne_pere,$id_utilisateur) ? true : false; $requete = 'INSERT INTO cel_mots_cles'.$this->suffixe_table.' '. 'VALUES ( '. Cel::db()->proteger($id_mot_cle).', '. Cel::db()->proteger($id_utilisateur).', '. Cel::db()->proteger($mot_cle).', '. Cel::db()->proteger($id_mot_cle_general).', '. Cel::db()->proteger($bg).', '. Cel::db()->proteger($bd).', '. Cel::db()->proteger($niveau).', '. Cel::db()->proteger($id_parent).') ' ; $transaction_reussie_2 = Cel::db()->executer($requete); if ($transaction_reussie_1 && $transaction_reussie_2) { // on sort de self::createElement ==> JRest::(get|post) ==> JRest->created() qui fait header(). // or si nous dépassons ini_get(output_buffering) nous ne pouvons plus réécrire le code de retour // HTTP, de plus, si ini_get(output_buffering) == off, nous enverrions un warning. // d'où ce clone de JRest::created(); header('HTTP/1.0 201 Created'); self::completerTransaction(); exit; } else { // cf ci-dessus: JRest::badRequest header('HTTP/1.0 400 Bad Request'); self::annulerTransaction(); exit; } return true; } public function deleteElement($uid) { $this->setChampsEtTablePourSuffixe($uid[0]); $id_utilisateur = $uid[1]; $id_mot_cle = $uid[2]; $tableau_ids_mots_cles = array(); $tableau_ids_mots_cles[] = $id_mot_cle; $this->controleUtilisateur($id_utilisateur); self::commencerTransaction(); $bornes = $this->calculerBornesEtNiveau($id_mot_cle, $id_utilisateur); if($bornes) { $bg = $bornes['bg']; $bd = $bornes['bd']; $requete_mots_cles_fils = 'SELECT id_mot_cle'.$this->suffixe_champ.' as id FROM cel_mots_cles'.$this->suffixe_table.' '. 'WHERE bg >= '.Cel::db()->proteger($bg).' '. ' AND bd <= '.Cel::db()->proteger($bd).' '. ' AND id_utilisateur = '.Cel::db()->proteger($id_utilisateur).' '; $mots_cles_fils = Cel::db()->requeter($requete_mots_cles_fils); foreach ($mots_cles_fils as $fils) { $tableau_ids_mots_cles[] = $fils['id']; } $requete = 'DELETE FROM cel_mots_cles'.$this->suffixe_table.' '. 'WHERE bg >= '.Cel::db()->proteger($bg).' '. ' AND bd <= '.Cel::db()->proteger($bd).' '. ' AND id_utilisateur = '.Cel::db()->proteger($id_utilisateur).' '; $transaction_reussie_1 = Cel::db()->executer($requete); $transaction_reussie_2 = $this->decalerBornesMoinsIntervalle($bg, $bd, $id_utilisateur) ? true : false; if ($transaction_reussie_1 !== false && $transaction_reussie_2 !== false) { self::completerTransaction(); } else { self::annulerTransaction(); } } // Suppression des liaisons associées à ce mot clé $gestion_liaisons = new LiaisonMotsCles($this->config, $this->suffixe); $gestion_liaisons->supprimerToutesLiaisonsPourIdMotCle($id_utilisateur, $tableau_ids_mots_cles); return true; } private function ajouterMotCleRacine($id) { $requete = 'SELECT COUNT(*) as nb_mc '. 'FROM cel_mots_cles'.$this->suffixe_table.' '. 'WHERE id_utilisateur = '.Cel::db()->proteger($id).' '; $resultat = Cel::db()->requeter($requete); if (is_array($resultat) && count($resultat) > 0) { $valeurs = $resultat[0]['nb_mc']; switch ($this->suffixe) { case 'obs' : $nom_racine = 'Projets'; $id_racine = 'racine_obs'; break; case 'images' : $nom_racine = 'Mots clés'; $id_racine = 'racine'; break; default: $nom_racine = $this->suffixe; $id_racine = $this->suffixe; } $md5_racine = Cel::db()->proteger(md5($nom_racine)); $id_racine = Cel::db()->proteger($id_racine); $nom_racine = Cel::db()->proteger($nom_racine); $id_utilisateur = Cel::db()->proteger($id); if ($valeurs == 0) { $requete = "INSERT INTO cel_mots_cles{$this->suffixe_table} ". "VALUES ($id_racine, $id_utilisateur, $nom_racine, $md5_racine, ". "1, 2, 0, '') "; Cel::db()->executer($requete); } } } /** * Désactive l'auto-commit puis débute la transaction */ static function commencerTransaction() { // Désactive l'autocommit le temps de la manipulation de l'arbre $reussite_autocommit = Cel::db()->executer("SET AUTOCOMMIT = 0"); // Débute une nouvelle transaction $reussite_begin = Cel::db()->executer("BEGIN"); } /** * Termine la transaction puis réactive l'auto-commit */ static function completerTransaction() { // Complète la transaction $reussite_commit = Cel::db()->executer("COMMIT"); // Réactive l'autocommit le temps de la manipulation de l'arbre $reussite_autocommit = Cel::db()->executer("SET AUTOCOMMIT = 1"); echo 'OK'; } /** * Annule la transaction et réactive l'auto-commit */ static function annulerTransaction() { // Annule la transaction $reussite_rollback = Cel::db()->executer("ROLLBACK"); // Réactive l'autocommit le temps de la manipulation de l'arbre $reussite_autocommit = Cel::db()->executer("SET AUTOCOMMIT = 1"); echo 'ERROR'; } /** * Renvoie les bornes d'un noeud de l'arbre des mots clés */ private function calculerBornesEtNiveau($id_mot_cle,$id_utilisateur) { $requete = 'SELECT bd, bg, niveau '. 'FROM cel_mots_cles'.$this->suffixe_table.' '. 'WHERE id_mot_cle'.$this->suffixe_champ.' = '.Cel::db()->proteger($id_mot_cle).' '. ' AND id_utilisateur = '.Cel::db()->proteger($id_utilisateur).' '; $resultat = Cel::db()->requeter($requete); $valeurs = null; if(is_array($resultat) && count($resultat) > 0) { $valeurs = $resultat[0]; } return $valeurs; } /** * Décale les bornes de deux pour insérer un nouvel élément */ private function decalerBornesPlusDeux($valeur, $id_utilisateur) { // Décalage borne droite $requete = 'UPDATE cel_mots_cles'.$this->suffixe_table.' '. 'SET bd = bd + 2 WHERE bd >= '.$valeur.' '. ' AND id_utilisateur = '.Cel::db()->proteger($id_utilisateur).' '; $reussi_1 = Cel::db()->executer($requete); // Décalage borne gauche $requete = 'UPDATE cel_mots_cles'.$this->suffixe_table.' '. 'SET bg = bg + 2 '. 'WHERE bg >= '.$valeur.' '. ' AND id_utilisateur = '.Cel::db()->proteger($id_utilisateur).' '; $reussi_2 = Cel::db()->executer($requete); return $reussi_1 !== false && $reussi_2 !== false; } /** * Décale les bornes d'un intervalle negatif donne (pour la suppression d'un sous arbre). */ private function decalerBornesMoinsIntervalle($bg, $bd, $id_utilisateur) { $decalage = $bd - $bg + 1; // Décalage borne droite $requete = 'UPDATE cel_mots_cles'.$this->suffixe_table.' '. 'SET bd = bd - '.$decalage.' '. 'WHERE bd >= '.$bg.' '. ' AND id_utilisateur = '.Cel::db()->proteger($id_utilisateur).' '; $reussi_1 = Cel::db()->executer($requete); // Décalage borne gauche $requete = 'UPDATE cel_mots_cles'.$this->suffixe_table.' '. 'SET bg = bg - '.$decalage.' '. 'WHERE bg > '.$bg.' '. ' AND id_utilisateur = '.Cel::db()->proteger($id_utilisateur).' '; $reussi_2 = Cel::db()->executer($requete); return $reussi_1 !== false && $reussi_2 !== false; } /** * Décale à droite des bornes donées d'un intervalle positif donne (pour l'ajout d'un sous arbre). */ private function decalerBornesPlusIntervalle($valeur_bornes, $largeur, $id_utilisateur) { $decalage = $largeur; // decalage borne droite $requete = 'UPDATE cel_mots_cles'.$this->suffixe_table.' '. 'SET bd = bd + '.$decalage.' '. 'WHERE bd >= '.$valeur_bornes.' '. ' AND id_utilisateur = '.Cel::db()->proteger($id_utilisateur).' '; $reussi_1 = Cel::db()->executer($requete); // decalage borne gauche $requete = 'UPDATE cel_mots_cles'.$this->suffixe_table.' '. 'SET bg = bg + '.$decalage.' '. 'WHERE bg >= '.$valeur_bornes.' '. ' AND id_utilisateur = '.Cel::db()->proteger($id_utilisateur).' '; $reussi_2 = Cel::db()->executer($requete); return $reussi_1 !== false && $reussi_2 !== false; } /** * Inverse les bornes d'un intervalle pour l'exclure des modifications sur l'arbre sans changer la hiérarchie. */ private function exclureIntervalle($bg, $bd, $id_utilisateur) { $requete = 'UPDATE cel_mots_cles'.$this->suffixe_table.' '. 'SET bd = bd - '.$bd.' - 1 , '. ' bg = bg - '.$bd.' - 1 '. 'WHERE bd <= '.$bd.' '. ' AND bg >= '.$bg.' '. ' AND id_utilisateur = '.Cel::db()->proteger($id_utilisateur).' '; return Cel::db()->executer($requete); } /** * Recale les bornes dun intervalle pour l'inclure dans l'arbre à la bonne place. * Décalage borne droite */ private function inclureIntervalle($bg, $bd, $decalage,$modif_niveau, $id_utilisateur) { $requete = 'UPDATE cel_mots_cles'.$this->suffixe_table.' '. 'SET bg = bg + '.$decalage.' , '. ' bd = bd + '.$decalage.', '. ' niveau = niveau + '.$modif_niveau.' '. ' WHERE bg >= '.$bg.' '. ' AND bd <= '.$bd.' '. ' AND id_utilisateur = '.Cel::db()->proteger($id_utilisateur).' '; return Cel::db()->executer($requete); } private function changerPere($id_mot_cle, $id_pere, $id_utilisateur) { $requete = 'UPDATE cel_mots_cles'.$this->suffixe_table.' '. 'SET ce_mot_cle'.$this->suffixe_champ.'_parent = '.Cel::db()->proteger($id_pere).' '. 'WHERE id_mot_cle'.$this->suffixe_champ.' = '.Cel::db()->proteger($id_mot_cle).' '. ' AND id_utilisateur = '.Cel::db()->proteger($id_utilisateur).' '; return Cel::db()->executer($requete); } } ?>