diff --git a/.gitignore b/.gitignore index 89b99b4..2bd5550 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ /tmp/**/* /tmp/* +/build/lightdb/storage/* +/build/lightdb/storage/**/* \ No newline at end of file diff --git a/build/lightdb/core/lightdb.php b/build/lightdb/core/lightdb.php new file mode 100644 index 0000000..3cf2a8f --- /dev/null +++ b/build/lightdb/core/lightdb.php @@ -0,0 +1,455 @@ + CREER LES FICHIERS S'ILS N'EXISTENT PAS SINON, RECUPERE LES DONNES + * + * @dbname Nom de la base de données + * + */ + public function __construct($dbname, $root=null){ + /* [0] On récupère les attributs + =========================================================*/ + $this->root = is_null($root) ? self::default_root().'/' : $root; + $this->dbname = $dbname; + $this->dir = $this->root.$dbname.'/'; + + + /* [1] Création du répertoire s'il n'existe pas + =========================================================*/ + if( !is_dir($this->dir) ) + mkdir($this->dir); + + /* [2] Création du fichier d'index ou récupération + =========================================================*/ + /* (1) Si le fichier n'existe pas, on le crée */ + if( !file_exists($this->dir.'index') ){ + $fIndex = new \SplFileObject($this->dir.'index', 'w'); + $fIndex->fwrite('[]'); + $fIndex = null; + } + + + /* (2) On récupère le contenu du fichier */ + $fIndex = new \SplFileObject($this->dir.'index'); + $fIndex->seek(0); + + $index = json_decode( $fIndex->fgets(), true ); + + // Si erreur de parsage, on retourne une erreur + if( is_null($index) ) + throw new \Exception("Cannot parse index"); + + $this->index = $index; + + /* [3] Initialisation du gestionnaire d'acces (SplFileObject) + =========================================================*/ + /* (1) Si le fichier n'existe pas, on le crée */ + if( !file_exists($this->dir.'data') ) + file_put_contents($this->dir.'data', '' ); + + /* (2) On place un 'driver' sur le fichier */ + $this->driver = new \SplFileObject($this->dir.'data', 'r+'); + // $this->driver->setFlags( \SplFileObject::SKIP_EMPTY ); + + /* (3) On récupère le nombre de lignes */ + $this->line = -1; + while( !$this->driver->eof() ){ + $this->line++; + $this->driver->fgetcsv(); + } + } + + + public function close(){ $this->driver = null; } + + + + /* [x] Flush the database + * + =========================================================*/ + public function flush(){ + file_put_contents($this->dir.'index', '[]' ); + file_put_contents($this->dir.'data', '' ); + } + + + /* RETOURNE LA LISTE DES INDEX + * + * @i Index pour lequel on veut la ligne et le hash + * + * @return Index Tableau associatif contenant le hash et la ligne + * + */ + public function index($i=null){ + if( is_null($i) ) + return $this->index; + + return isset($this->index[$i]) ? $this->index[$i] : NULL; + } + + + /* INSERTION D'UNE ENTREE DANS LA BASE DE DONNEES + * + * @key Clé qui permettra l'accès direct + * @data Objet qui sera enregistré dans la base + * + * @return status Retourne TRUE si tout s'est bien passé, sinon FALSE + * + */ + public function insert($key, $data){ + /* (1) On vérifie que la clé est unique */ + if( array_key_exists($key, $this->index) ) + return true; + + $key = (string) $key; + + /* (2) On ajoute les données aux fichier */ + $json_data = json_encode($data); + $this->driver->seek($this->line); + $this->line++; + $written = $this->driver->fwrite( $json_data.PHP_EOL ); + + // Si erreur d'écriture, on retourne FALSE + if( is_null($written) ) + return false; + + /* (3) On enregistre l'index */ + $this->index[$key] = [ + 'line' => $this->line - 1, + 'hash' => sha1($json_data) + ]; + + /* (4) On enregistre le fichier index */ + $fIndex = new \SplFileObject($this->dir.'index', 'w'); + $fIndex->fwrite( json_encode($this->index) ); + $fIndex = null; + + return true; + } + + + /* INSERTION D'UNE ENTREE DANS LA BASE DE DONNEES + * + * @dataset Tableau de 'clés'->'valeurs' à insérer + * @data Objet qui sera enregistré dans la base + * + * @return status Retourne TRUE si tout s'est bien passé, sinon FALSE + * + */ + public function insertAll($dataset){ + /* (1) On vérifie que la clé est unique */ + foreach($dataset as $key=>$data) + if( array_key_exists($key, $this->index) ) + unset($dataset[$key]); + + + /* (2) On ajoute les données aux fichier */ + $this->driver->seek($this->line); + foreach($dataset as $key=>$data){ + $json_data = json_encode($data); + $this->line++; + $written = $this->driver->fwrite( $json_data.PHP_EOL ); + + + /* (3) On enregistre les index */ + $this->index[$key] = [ + 'line' => $this->line - 1, + 'hash' => sha1($json_data) + ]; + } + + + + /* (4) On enregistre le fichier index */ + $fIndex = new \SplFileObject($this->dir.'index', 'w'); + $fIndex->fwrite( json_encode($this->index) ); + $fIndex = null; + + return true; + } + + + /* RENVOIE LES DONNEES ASSOCIEES A UNE CLE DONNEE + * + * @key Clé associée à la valeur à récupérer + * + * @return data Renvoie la valeur associée à la clé, FALSE si erreur + * + */ + public function fetch($key){ + /* (1) On vérifie que la clé existe bien */ + if( !array_key_exists($key, $this->index) ) + return false; + + /* (2) On récupère la ligne */ + $line = $this->index[$key]['line']; + + /* (3) On récupère le contenu */ + $this->driver->seek($line); + $json = json_decode( $this->driver->current(), true ); + + // Si erreur de parsage + if( is_null($json) ) + return false; + + return $json; + } + + + + + /* RENVOIE LES DONNEES ASSOCIEES AUX CLES DONNEES + * + * @keys Clés associées aux valeurs à récupérer + * + * @return data Renvoie les valeurs associées aux clé, ou un tableau vide si erreur + * + */ + public function fetchAll($keys){ + $data = []; + + /* (0) Pour chaque clé */ + foreach($keys as $i=>$key){ + + /* (1) On ne prend pas en compte les clés qui n'existent pas */ + if( !array_key_exists($key, $this->index) ) + continue; + + /* (2) On récupère la ligne */ + $line = $this->index[$key]['line']; + + /* (3) On récupère le contenu */ + $this->driver->seek($line); + $json = json_decode( $this->driver->current(), true ); + + /* (4) Si pas d'erreur de parsage, On enregistre */ + if( !is_null($json) ) + $data[$key] = $json; + + } + + + + return $data; + } + + + /* SUPPRIME UNE ENTREE DE CLE DONNEE DE LA BASE DE DONNEES + * + * @key Clé de l'entrée à supprimer + * + * @return status Retourne TRUE si tout s'est bien passé, sinon FALSE + * + */ + public function delete($key){ + /* (1) On vérifie l'existence de la clé */ + if( !array_key_exists($key, $this->index) ) + return true; // On considère que l'action souhaitée est effectuée + + $line = $this->index[$key]['line']; + + /* (2) On réarrange la bd pour supprimer la ligne */ + $tmpfilename = __ROOT__.'/tmp/'.uniqid().'.dat'; + $tmpfile = new \SplFileObject($tmpfilename, 'w'); + $this->driver->seek(0); + + // On recopie toutes les lignes sauf celle à supprimer dans un fichier temporaire + while( $this->driver->key() < $this->line ){ + + if( $this->driver->key() != $line ) + $tmpfile->fwrite( $this->driver->current() ); + + $this->driver->next(); + } + + // On décrémente le nb de lignes + $this->line--; + + $tmpfile = null; + + /* (3) On remplace le fichier original par le fichier temporaire */ + $this->driver = null; + rename($tmpfilename, $this->dir.'data'); + $this->driver = new \SplFileObject($this->dir.'data', 'r+'); + + /* (3) On supprime la ligne de l'index */ + unset( $this->index[$key] ); + + /* (4) On met à jour les index des lignes déplacées */ + foreach($this->index as $i=>$indexData) + if( $indexData['line'] > $line ) + $this->index[$i]['line']--; // on décrémente les lignes au dessus de la ligne supprimée + + + /* (5) On enregistre le fichier index */ + $fIndex = new \SplFileObject($this->dir.'index', 'w'); + $fIndex->fwrite( json_encode($this->index) ); + $fIndex = null; + + + return true; + } + + + + /* SUPPRIME PLUSIEURS ENTREES DE CLES DONNEES DE LA BASE DE DONNEES + * + * @keys Clés des entrées à supprimer + * + * @return status Retourne TRUE si tout s'est bien passé, sinon FALSE + * + */ + public function deleteAll($keys){ + $keyLines = []; + + /* [1] On récupère la ligne associée à chaque clé + =========================================================*/ + foreach($keys as $k=>$key){ + /* (1) Si la clé n'existe pas, on passe à la suivante */ + if( !array_key_exists($key, $this->index) ) + continue; + + /* (2) On récupère la ligne de la clé */ + $keyLines[$key] = $this->index[$key]['line']; + } + + /* [2] On trie les clés en fonction de leur ligne + =========================================================*/ + $sorted = []; + + // Tant que toute les clés ne sont pas triées + while( count($keyLines) > 0 ){ + // Contiendra la clé de la plus petite valeur + $min = null; + + // On cherche la ligne la plus petite + foreach($keyLines as $key=>$line) + if( is_null($min) || $line < $keyLines[$min] ) // Si valeur inf à min + $min = $key; + + // On ajoute la plus petite clé trouvée a la liste + $sorted[$min] = $keyLines[$min]; + + // On la supprime du tableau à trier + unset($keyLines[$min]); + + } + + /* [3] On supprime les lignes à supprimer + =========================================================*/ + /* (1) On réarrange la bd pour supprimer la ligne */ + $tmpfilename = __ROOT__.'/tmp/'.uniqid().'.dat'; + $tmpfile = new \SplFileObject($tmpfilename, 'w'); + $this->driver->seek(0); + + /* (2) On recopie toutes les lignes sauf celles à supprimer dans un fichier temporaire */ + while( $this->driver->key() < $this->line ){ + + // Si la ligne en cours n'est pas dans la liste des lignes à supprimer + if( !in_array($this->driver->key(), $sorted) ) + $tmpfile->fwrite( $this->driver->current() ); // On l'écrit dans le nouveau fichier + + $this->driver->next(); + } + + $tmpfile = null; + + /* (3) On remplace le fichier original par le fichier temporaire */ + $this->driver = null; + rename($tmpfilename, $this->dir.'data'); + $this->driver = new \SplFileObject($this->dir.'data', 'r+'); + + + /* [4] On met à jour les index + =========================================================*/ + $step = 0; + foreach($sorted as $key=>$line){ + + /* (1) On décrémente le nb de lignes */ + $this->line--; + + /* (2) On supprime la ligne de l'index */ + unset( $this->index[$key] ); + + /* (3) On met à jour les index des lignes déplacées du nombre d'index qu'on a supprimé */ + foreach($this->index as $i=>$indexData) + if( $indexData['line'] > $line-$step ) + $this->index[$i]['line']--; // on décrémente les lignes au dessus de la ligne supprimée + + $step++; + } + + /* (4) On enregistre le fichier index */ + $fIndex = new \SplFileObject($this->dir.'index', 'w'); + $fIndex->fwrite( json_encode($this->index) ); + $fIndex = null; + + + return true; + } + + + + + + + /* RENVOIE LES DONNEES ASSOCIEES A UN CHAMP DE RECHERCHE + * + * @nomParam Description du param + * + * @return nomRetour Description du retour + * + */ + public function filter($data){ + /* (1) Si @data est un tableau associatif */ + if( is_array($data) ){ + + $filtered = []; + foreach($this->index as $i=>$indexData){ + $this->driver->seek( $indexData['line'] ); + $dbData = json_decode( $this->driver->fgets(), true ); + + foreach($data as $key=>$value) + if( isset($dbData[$key]) && preg_match("#$value#", $dbData[$key]) ){ + $filtered[$i] = $dbData; + break; + } + } + + return $filtered; + + + /* (2) Sinon on compare @data en tant que valeur simple */ + }else{ + + $this->tmp = sha1( json_encode($data) ); + return array_filter($this->index, [$this, 'simpleFilter']); + + } + + } + protected function simpleFilter($e){ return $e['hash'] == $this->tmp; } + + + + + + + } diff --git a/build/router/controller/ics.php b/build/router/controller/ics.php index 24451e2..aa326fb 100644 --- a/build/router/controller/ics.php +++ b/build/router/controller/ics.php @@ -2,6 +2,8 @@ namespace router\controller; + use \lightdb\core\lightdb; + class ics{ @@ -15,6 +17,7 @@ */ public function __construct($url){ $this->diplome_id = $url['diplome_id']; + } @@ -45,6 +48,40 @@ } + + + public function info(){ + + /* [1] Get database colors + =========================================================*/ + /* (1) Open instance */ + try{ + $ldb = new lightdb('config'); + }catch(\Exception $e){ + die("An error occured. Please contact the developers.\n"); + } + + /* (2) Fetch diplome data */ + $d_cols = $ldb->fetch($this->diplome_id); + + /* (3) Error */ + if( $d_cols == false ) + die("No color found for this diplome"); + + + foreach($d_cols as $col=>$name){ + + echo "PLANNING COLOR => "; + + if( $col == $name ) + echo "no name
"; + else + echo "$name
"; + + } + + } + /* POST-CALL * */ diff --git a/build/router/controller/page.php b/build/router/controller/page.php index 550c04f..a5524d5 100644 --- a/build/router/controller/page.php +++ b/build/router/controller/page.php @@ -41,7 +41,7 @@ /* [2] Display the links =========================================================*/ - echo ""; + echo "
DiplomeLink
"; foreach($diplomes as $id=>$name){ @@ -49,9 +49,12 @@ echo ""; $link = $_SERVER['HTTP_HOST']."/ics/$id.ics"; + echo ""; + echo ""; + echo " href='/ics/$id.ics'"; + echo ">https://$link"; echo ""; } diff --git a/build/service/CalendarExtractor.php b/build/service/CalendarExtractor.php index 6d411da..ec1e291 100644 --- a/build/service/CalendarExtractor.php +++ b/build/service/CalendarExtractor.php @@ -2,6 +2,8 @@ namespace service; + use \lightdb\core\lightdb; + class CalendarExtractor{ @@ -10,9 +12,9 @@ =========================================================*/ private $start_d = null; // 1st day date private $img_url = null; // image url + private $d_uid = null; // diplome uid private $img_res = null; // image resource private $event = []; // events - private $event_t = [ 0 ]; // types of events /* [2] Constants =========================================================*/ @@ -25,31 +27,36 @@ /* (1) Builds a calendar extractor * + * @d_uid UID of the diplome * @img_url URL of the string to extract from * @start_d Date of the first day * * @return instance Instance * ---------------------------------------------------------*/ - public function __construct($img_url=null, $start_d=null){ + public function __construct($d_uid=null, $img_url=null, $start_d=null){ /* [1] Check arguments =========================================================*/ { /* (1) Check type */ if( !is_string($img_url) || !is_string($start_d) ) - throw new \Exception("CalendarExtractor.__construct(, ) expected but CalendarExtractor.__construct(<".gettype($img_url).">, <".gettype($start_d).">) received"); + throw new \Exception("CalendarExtractor.__construct(, , ) expected but CalendarExtractor.__construct(<".gettype($d_uid).">, <".gettype($img_url).">, <".gettype($start_d).">) received"); /* (2) Check @img_url link availability */ if( !($img_head=@get_headers($img_url)) ) - throw new \Exception("CalendarExtractor.__construct(, ) received but cannot reach "); + throw new \Exception("CalendarExtractor.__construct(, , ) received but cannot reach "); if( !preg_match('@HTTP.+200@', $img_head[0]) ) - throw new \Exception("CalendarExtractor.__construct(, ) received but cannot reach "); + throw new \Exception("CalendarExtractor.__construct(, , ) received but cannot reach "); /* (3) Check @start_d format */ if( !preg_match("@^\d{1,2}-\d{1,2}-\d{3,}$@", $start_d) ) - throw new \Exception("CalendarExtractor.__construct(, ) received has not the correct format"); + throw new \Exception("CalendarExtractor.__construct(, , ) received has not the correct format"); + + /* (4) Check @d_uid format */ + if( !preg_match("@^T\d+$@", $d_uid) ) + throw new \Exception("CalendarExtractor.__construct(, , ) received has not the correct format"); } @@ -67,6 +74,7 @@ /* (3) Register data */ $this->img_url = $img_url; + $this->d_uid = $d_uid; $this->start_d = $start_d; } @@ -101,6 +109,27 @@ ]; + /* (2) Light database + ---------------------------------------------------------*/ + + /* (1) Create/Open database */ + try{ + $ldb = new lightdb('config'); + + /* (2) Manage error */ + }catch(\Exception $e){ + throw new \Exception("Cannot access database"); + } + + /* (3) Fetch diplome data */ + $d_db = $ldb->fetch($this->d_uid); + + /* (4) Manage error */ + if( $d_db == false ) + $d_db = []; + + + } } @@ -142,6 +171,7 @@ /* (3) Get current color + next */ $p = $this->getColor($col_x, $y); $p1 = $this->getColor($col_x, $y+1); + $p1e = '#'.dechex($p1); /* (4) If on black pixel and next not white */ if( $p == 0 && $p1 != 0xffffff ){ @@ -149,14 +179,12 @@ // {1} calculate time // $time = $this->yToTime($day_n, $y); - // {2} Store new event type (if missing) // - if( !isset($this->event_t[$p1]) ) - $this->event_t[$p1] = $this->event_t[0]++; - - $uid = $this->event_t[$p1]; + // {2} Store default event name if not already set // + if( !isset($d_db[$p1e]) ) + $d_db[$p1e] = $p1e; // {3} Store event start // - $this->event[$uid][$time] = []; + $this->event[$p1e][$time] = []; // {4} Seek end of event // $y++; @@ -164,7 +192,7 @@ $y++; // {5} If end reached // - $this->event[$uid][$time] = $this->yToTime($day_n, $y); + $this->event[$p1e][$time] = $this->yToTime($day_n, $y); } @@ -177,6 +205,17 @@ } + + /* [4] Save db changes + =========================================================*/ + /* (1) Update data */ + $ldb->delete($this->d_uid); + $ldb->insert($this->d_uid, $d_db); + + /* (2) Close connection */ + $ldb->close(); + + } @@ -268,19 +307,32 @@ public function toIcs(){ $RAW = ""; - /* [1] Manage types + + + /* [1] Get light-database =========================================================*/ - $type_name = []; + /* (1) Create/Open database */ + try{ + $ldb = new lightdb('config'); - foreach($this->event_t as $color=>$uid) - $type_name[$uid] = dechex($color); // hexa representation + /* (2) Manage error */ + }catch(\Exception $e){ + throw new \Exception("Cannot access database"); + } + /* (3) Get color association if available */ + $col_assoc = []; + if( !is_null($ldb->index($this->d_uid)) ) + $col_assoc = $ldb->fetch($this->d_uid); + + // var_dump($col_assoc); + // die(1); /* [2] For each event =========================================================*/ - foreach($this->event as $event_t=>$events){ + foreach($this->event as $event_col=>$events){ - $type = "Event of type '".$type_name[$event_t]."'"; + $type = $col_assoc[$event_col]; /* (2) For each event of each type ---------------------------------------------------------*/ diff --git a/build/service/Config.php b/build/service/Config.php index f47028c..8610bb5 100644 --- a/build/service/Config.php +++ b/build/service/Config.php @@ -1,7 +1,12 @@ insert('diplomes', $d_list); + $ldb->insert('periods', $p_list); + + /* (4) Close the database */ + $ldb->close(); + + } + + + + /* [x] Return the instance */ return new Config($d_list, $p_list); } @@ -195,28 +217,25 @@ /* [1] Load cache =========================================================*/ { - /* (1) Check files */ - if( !file_exists(__ROOT__."/tmp/diplomes.cache") ) - throw new \Exception("Diplomes cache is missing"); + /* (1) Open/Create light-database */ + try{ + $ldb = new lightdb("config"); - if( !file_exists(__ROOT__."/tmp/periods.cache") ) - throw new \Exception("Periods cache is missing"); + /* (2) Manage error */ + }catch(\Exception $e){ + throw new \Exception("Cannot open light-database"); + } + /* (3) Fetch data */ + $d_list = $ldb->fetch('diplomes'); + $p_list = $ldb->fetch('periods'); - /* (2) Read files */ - if( !($d_list=@file_get_contents(__ROOT__."/tmp/diplomes.cache")) ) - throw new \Exception("Diplomes cache is not readable"); + /* (4) Check error */ + if( $d_list == false || $p_list == false ) + throw new \Exception("Cannot read data"); - if( !($p_list=@file_get_contents(__ROOT__."/tmp/periods.cache")) ) - throw new \Exception("Periods cache is not readable"); - - - /* (3) Parse files */ - if( is_null(( $d_list = json_decode($d_list, true) )) ) - throw new \Exception("Cannot parse diplome list"); - - if( is_null(( $p_list = json_decode($p_list, true) )) ) - throw new \Exception("Cannot parse periods list"); + /* (5) Close the database */ + $ldb->close(); } diff --git a/build/service/Updater.php b/build/service/Updater.php index 9712abf..8bf69ee 100644 --- a/build/service/Updater.php +++ b/build/service/Updater.php @@ -50,7 +50,7 @@ foreach($this->config->getPeriods() as $p_id=>$p_date){ /* (1) Load image in the extractor */ - $calext = new CalendarExtractor("http://sciences.univ-pau.fr/edt/diplomes/${d_id}${p_id}.png", $p_date); + $calext = new CalendarExtractor($d_id, "http://sciences.univ-pau.fr/edt/diplomes/${d_id}${p_id}.png", $p_date); /* (2) Extract calendar data */ $calext->process(); diff --git a/config/routes.json b/config/routes.json index 34a9970..7db87fd 100644 --- a/config/routes.json +++ b/config/routes.json @@ -20,6 +20,14 @@ } }, + "/info/{diplome_id}": { + "methods": ["GET"], + "controller": "ics:info", + "arguments": { + "diplome_id": "T\\d+" + } + }, + "/{any}": { "methods": ["GET"], "controller": "redirect:homepage", diff --git a/public_html/index.php b/public_html/index.php index 86c88fd..65e534c 100644 --- a/public_html/index.php +++ b/public_html/index.php @@ -2,7 +2,7 @@ use \router\core\Router; - + debug(); $R = new Router( $_GET['url'] ); $R->run();
DiplomeConfigLink
$nameSet colorshttps://$link