Reprise projet été 2016

This commit is contained in:
xdrm-brackets 2016-07-02 17:10:41 +02:00
parent 7fd8ecf8e1
commit fadb8c5d76
27 changed files with 1279 additions and 275 deletions

View File

@ -3,4 +3,4 @@
"dbname" : "stefproject",
"user" : "php",
"password" : "QbzjZACndQM6NmuD"
}
}

View File

@ -1,10 +1,13 @@
<?php define('__ROOT__', dirname(__FILE__) );
require_once __ROOT__.'/manager/autoloader.php';
use \router\Router;
use \manager\ResourceDispatcher;
use \manager\ModuleRequest;
use \manager\ModuleResponse;
use \manager\ManagerError;
/*******************************************/
/* DEBUGGER */
@ -41,27 +44,24 @@
$R->get('f(?:/([\w-]+))*/?', function(){ new ResourceDispatcher($_GET['url'], true); });
// Api
$R->post('api/?', function(){
$request = ModuleRequest::fromPost($_POST);
$R->post('api(?:/(.*))?', function($url){
$request = ModuleRequest::fromPost($url, $_POST);
$answer = $request->dispatch();
echo $answer->serialize();
// Si c'est une réponse (et non un download)
if( $answer instanceof ModuleResponse )
echo $answer->serialize();
});
// N'importe -> page d'accueil
$R->get('.+', function(){ header('Location: /dashboard/'); });
$R->post('.+', function(){ header('Location: /dashboard/'); });
// $R->post('.*', function(){
// var_dump( 'Acces POST : '.$_GET['url'] );
// var_dump( $_POST );
// });
/* [2] On lance le routeur
===================================================*/
$R->run();
?>
?>

6
js/action-script-min.js vendored Normal file
View File

@ -0,0 +1,6 @@
DOM={WRAPPER:$("WRAPPER"),HEADER:$("HEADER"),MENUSIDE:$("MENU-SIDE"),CONTAINER:$("CONTAINER")};var pageManager=new pageManagerClass;pageManager.setPage(null,"/view",DOM.CONTAINER,"profile dashboard machines users groups analytics settings".split(" "));var api=new APIClass("/api/");
function navSubMenu(a){var c=document.querySelector('#CONTAINER > .sub-menu-side > span[data-sublink="'+pageManager.vars[0]+'"]'),b=null,b=a instanceof Element?a:null,b="string"==typeof a?document.querySelector('#CONTAINER > .sub-menu-side > span[data-sublink="'+a+'"]'):b,b=null==b?document.querySelector("#CONTAINER > .sub-menu-side > span[data-sublink]"):b;if(null==b)return!1;null!=c&&c.remClass("active");a=document.querySelectorAll("#CONTAINER > section[data-sublink].active");for(c=0;c<a.length;c++)a[c].remClass("active");
null!=b&&(b.addClass("active"),a=document.querySelector('#CONTAINER > section[data-sublink="'+b.getData("sublink")+'"]'),null!=a&&a.addClass("active"));if(!b.getData("sublink"))return!1;a=pageManager.vars[0]!=b.getData("sublink");pageManager.vars[0]=b.getData("sublink");a&&pageManager.updateURL()}
function navMenu(a){var c=document.querySelector('#WRAPPER > #MENU-SIDE > span[data-link="'+pageManager.page+'"]'),b=null,b=a instanceof Element?a:null,b="string"==typeof a?document.querySelector('#WRAPPER > #MENU-SIDE > span[data-link="'+a+'"]'):b,b=null==b?document.querySelector('#WRAPPER > #MENU-SIDE > span[data-link="'+pageManager.pagelist[0]+'"]'):b;if(null==b)return!1;null!=c&&c.remClass("active");null!=b&&b.addClass("active");b.getData("link")&&(DOM.HEADER.addClass("loading"),pageManager.setPage(b.getData("link")),
pageManager.activeXHR.addEventListener("loadend",function(){DOM.HEADER.remClass("loading");1<=pageManager.vars.length&&null!=document.querySelector('#CONTAINER > .sub-menu-side > [data-sublink="'+pageManager.vars[0]+'"]')?navSubMenu(pageManager.vars[0]):navSubMenu(null);document.querySelector("#CONTAINER > .sub-menu-side").addEventListener("click",function(a){for(a=a.target;a!=document.body&&!a.getData("sublink");)a=a.parentNode;a.getData("sublink")&&navSubMenu(a)},!1)},!1))}navMenu(pageManager.page);
DOM.MENUSIDE.addEventListener("click",function(a){for(a=a.target;a!=document.body&&!a.getData("link");)a=a.parentNode;a.getData("link")&&navMenu(a.getData("link"))},!1);

View File

@ -26,7 +26,7 @@ function navSubMenu(subsection){
var target = null;
// si @subsection est un element, on le prends
target = (subsection instanceof Element) ? subsection : null;
target = (subsection instanceof Element) ? subsection : null;
// Si string, on trouve l'element correspondant
target = (typeof subsection == 'string') ? document.querySelector('#CONTAINER > .sub-menu-side > span[data-sublink="'+subsection+'"]') : target;
@ -42,7 +42,7 @@ function navSubMenu(subsection){
// On desactive l'element courant
if( current != null )
current.remClass('active');
// On cache les sections visibles
var visibleSections = document.querySelectorAll('#CONTAINER > section[data-sublink].active');
for( var i = 0 ; i < visibleSections.length ; i++ )
@ -86,7 +86,7 @@ function navSubMenu(subsection){
/* [2] Toggle du side-menu <-> navigation
===========================================*/
function navMenu(section){
/* [1] Format du param
------------------------------------------------*/
// Contient l'element courant
@ -96,7 +96,7 @@ function navMenu(section){
var target = null;
// si @section est un element, on le prends
target = (section instanceof Element) ? section : null;
target = (section instanceof Element) ? section : null;
// Si string, on trouve l'element correspondant
target = (typeof section == 'string') ? document.querySelector('#WRAPPER > #MENU-SIDE > span[data-link="'+section+'"]') : target;
@ -188,6 +188,3 @@ DOM.MENUSIDE.addEventListener('click', function(e){
if( target.getData('link') )
navMenu(target.getData('link'));
}, false);

4
js/lib/api-min.js vendored Normal file
View File

@ -0,0 +1,4 @@
function APIClass(c){this.target=c}
APIClass.prototype={xhr:[],buffer:null,optionalParams:[],send:function(c,f,g){c.hasOwnProperty("path")||f({ModuleError:4});for(var a=0;a<this.xhr.length;a++)4==this.xhr[a].readyState&&this.xhr.splice(a,1);this.xhr.push(null);a=this.xhr.length-1;this.optionalParams[a]=[];if(3<arguments.length)for(var d=3;d<arguments.length;d++)this.optionalParams[a].push(arguments[d]);this.xhr[a]=window.XMLHttpRequest?new XMLHttpRequest:new ActiveXObject("Microsoft.XMLHttpRequest");var e=this;this.xhr[a].onreadystatechange=
function(){if(4==e.xhr[a].readyState)if(e.buffer=e.xhr[a].responseText,console.log("api request",c),-1<[0,200].indexOf(e.xhr[a].status))try{f(JSON.parse(e.xhr[a].responseText),e.optionalParams[a])}catch(b){f({ModuleError:-1,ErrorDescription:"Erreur au niveau de api.js"},e.optionalParams[a]),console.warn(b)}else f({ModuleError:3})};var d=new FormData,b;for(b in c)"path"==b?d.append(b,c[b]):c[b]instanceof File?d.append(b,c[b]):d.append(b,JSON.stringify(c[b]));this.xhr[a].open("POST",this.target,!0);
null!=g&&this.xhr[a].setRequestHeader("Authorization","Digest "+g);this.xhr[a].setRequestHeader("X-Requested-With","XMLHttpRequest");this.xhr[a].send(d)}};

View File

@ -1,83 +1,111 @@
/* classe API */
function APIClass(target){ this.target = target; };
function APIClass(target){ this.target = target; }
APIClass.prototype = {
xhr: [], // tableau d'objets pour les requêtes ajax
buffer: null, // Contiendra le buffer pour debugger si erreur de parsage
optionalParams: [], // Contiendra les paramètres que l'on veut passer au scope de @pHandler
/* transaction avec le serveur (http://host/api/)
*
* @param pRequest<Object> l'objet passé en JSON à http://host/api/
* @param pRequest<Object> l'objet passé en POST (attribut->postfield) à http://host/api/
* @param pHandler<Function> fonction qui s'éxécutera lors de la réponse (1 argument -> réponse<Object>)
* @param pToken<String> Optionnel, token d'auth pour l'api
* @param pParams<Mixed> Optionnels, liste d'arguments à passer au scope de @pHandler
*
* @return answer<Object> l'objet retourné par http://host/api/ via pHandler (1er argument)
* @return answer<Object> l'objet retourné par http://host/api/ via pHandler (1er argument)
*
***************************************************************************************************
*
* @usecase
* 1. var answerObject = sendRequest(
* 2. { var1: "exemple", var2: 198294 },
* 3. function(rep){ alert(rep); }
* 2. { var1: "exemple", var2: 198294 },
* 3. function(rep){ alert(rep); }
* 4. );
* @explain
* 1. on appelle la fonction <=> on créé la requête
* 2. on passe l'objet qui sera envoyé
* 1. on appelle la fonction <=> on créé la requête
* 2. on passe l'objet qui sera envoyé
* 3. on passe une fonction qui utilise un argument (sera la réponse de http://host/api/) (sous forme d'objet)
*
*/
send: function(pRequest, pHandler){
send: function(pRequest, pHandler, pToken){
// Si le chemin de delegation n'est pas renseigne, on renvoie une erreur
if( !pRequest.hasOwnProperty('path') )
pHandler({ModuleError:4});
// on efface les requêtes qui sont terminées (toutes celles de this.xhr)
for( var i = 0 ; i < this.xhr.length ; i++ ){
for( var i = 0 ; i < this.xhr.length ; i++ )
if( this.xhr[i].readyState == 4 ) // si terminée
this.xhr = this.xhr.slice(0,i-1).concat(this.xhr.slice(i,this.xhr.length-1)); // suppression entrée
}
this.xhr.splice(i, 1);
// on créé une nouvelle entrée
this.xhr.push(null);
i = this.xhr.length-1;
// Gestion des paramètres optionnels à passer au scope de @pHandler
this.optionalParams[i] = [];
if( arguments.length > 3 )
for( var arg = 3 ; arg < arguments.length ; arg++ )
this.optionalParams[i].push( arguments[arg] );
// création de l'objet AJAX
if(window.XMLHttpRequest) // IE7+, Firefox, Chrome, Opera, Safari
this.xhr[i] = new XMLHttpRequest();
else // IE5, IE6
else // IE5, IE6
this.xhr[i] = new ActiveXObject('Microsoft.XMLHttpRequest');
console.log(pRequest);
var ptrAPI = this;
this.xhr[i].onreadystatechange = function(){
if( ptrAPI.xhr[i].readyState == 4 ){ // si la requête est terminée
ptrAPI.buffer = ptrAPI.xhr[i].responseText;
/* DEBUG : affiche la réponse BRUTE de http://host/api/ */
// console.log('http://host/api/ => '+ptrAPI.xhr[i].responseText);
// console.log( JSON.parse(ptrAPI.xhr[i].responseText) );
console.log('api request', pRequest);
/* si success de requête */
if( [0,200].indexOf(ptrAPI.xhr[i].status) > -1 ){ // si fichier existe et reçu
try{ pHandler( JSON.parse(ptrAPI.xhr[i].responseText) ); } // si on peut parser, on envoie
catch(e){ pHandler({ModuleError:1}); } // sinon on envoie obj.request = 'corrupted'
try{ pHandler( JSON.parse(ptrAPI.xhr[i].responseText), ptrAPI.optionalParams[i]); } // si on peut parser, on envoie
catch(e){ pHandler({ModuleError:-1, ErrorDescription:'Erreur au niveau de api.js'}, ptrAPI.optionalParams[i]); console.warn(e); } // sinon on envoie obj.request = 'corrupted'
}
/* sinon retourne obj.request = 'unreachable' */
else
pHandler({ModuleError:3});
}
}
};
// on créé un formulaire POST (virtuel)
var form = new FormData();
form.append('path', pRequest.path ); // on créé la variable $_POST['json']=>request
if( pRequest.hasOwnProperty('data') )
form.append('data', JSON.stringify(pRequest.data) );
// On ajoute tous les attributs en POST
for( var key in pRequest )
// On envoie le 'path' tel quel <String>
if( key == 'path' ) form.append(key, pRequest[key]);
// On envoie un fichier tel quel <File>
else if( pRequest[key] instanceof File ) form.append(key, pRequest[key]);
// On envoie le reste en JSON
else form.append(key, JSON.stringify(pRequest[key]));
this.xhr[i].open('POST', this.target, true);
// Gestion du token optionnel
if( pToken != null ) this.xhr[i].setRequestHeader('Authorization', 'Digest '+pToken);
// Header pour dire que c'est AJAX
this.xhr[i].setRequestHeader('X-Requested-With', 'XMLHttpRequest');
this.xhr[i].send( form );
}

5
js/lib/form-deflater-min.js vendored Normal file
View File

@ -0,0 +1,5 @@
function FormDeflater(b,c,a){for(var d=0;d<c.length;d++)c[d]=c[d].toLowerCase();for(d=0;d<a.length;d++)a[d]=a[d].toLowerCase();this.container=b;this.tags=c;this.attr=a}FormDeflater.prototype={container:this.container,tags:this.tags,attr:this.attr};
FormDeflater.prototype.deflate=function(){for(var b=this.getChildren(this.container),b=this.filterElements(b),c={},a=0;a<b.length;a++)for(var d=0;d<this.attr.length;d++){var e=b[a].getAttribute(this.attr[d]);if(null!==e&&0<e.length){c.hasOwnProperty(e)?c[e]instanceof Array?c[e].push({target:b[a],attr:this.attr[d],value:b[a].value,checked:b[a].checked}):c[e]=[c[e],{target:b[a],attr:this.attr[d],value:b[a].value,checked:b[a].checked}]:c[e]={target:b[a],attr:this.attr[d],value:b[a].value,checked:b[a].checked};
break}}return c=this.cleanOutput(c)};FormDeflater.prototype.checkable=function(b){return"INPUT"!=b.tagName||-1==["radio","checkbox"].indexOf(b.getAttribute("type").toLowerCase())?!1:!0};
FormDeflater.prototype.cleanOutput=function(b){var c={},a;for(a in b)if(b[a]instanceof Array){var d=!0,e=[],f;for(f in b[a])if(this.checkable(b[a][f].target))!0===b[a][f].checked&&e.push(f);else{d=!1;break}if(d)if(1==e.length)c[a]=b[a][e[0]].value;else for(f in c[a]=[],e)c[a].push(b[a][e[f]].value);else for(f in c[a]=[],b[a])c[a].push(b[a][f].value)}else this.checkable(b[a].target)?c[a]=b[a].checked?b[a].value:null:c[a]=b[a].value;return c};
FormDeflater.prototype.getChildren=function(b){if(!(b instanceof Element))return[];for(var c=b=[].slice.call(b.children),a=0;a<b.length;a++)c=c.concat([].slice.call(this.getChildren(b[a])));return c};FormDeflater.prototype.filterElements=function(b){for(var c=[],a=0;a<b.length;a++)-1<this.tags.indexOf(b[a].tagName.toLowerCase())&&c.push(b[a]);return c};

285
js/lib/form-deflater.js Normal file
View File

@ -0,0 +1,285 @@
/* CONSTRUCTEUR D'UN DEFLATER DE formulaire
*
* @container<Element> Formulaire ou autre élément contenant les champs
* @tags<Array> Tableau contenant les éléments à prendre en compte
* @attr<Array> Tableau contenant les attributs à prendre pour le nom (par ordre de priorité)
*
*/
function FormDeflater(container, tags, attr){
/* [0] Vérification des INPUT
=========================================================*/
var correctParams = container instanceof Element;
correctParams = correctParams && tags instanceof Array;
correctParams = correctParams && attr instanceof Array;
/* [1] On formatte les données
=========================================================*/
// On met les tags en minuscule
for( var i = 0 ; i < tags.length ; i++ )
tags[i] = tags[i].toLowerCase();
// On met les attributs en minuscule
for( var i = 0 ; i < attr.length ; i++ )
attr[i] = attr[i].toLowerCase();
/* [2] On enregistre les attributs
=========================================================*/
this.container = container;
this.tags = tags;
this.attr = attr;
}
FormDeflater.prototype = {
container: this.container, // Contiendra le 'formulaire' (<form> ou autre)
tags: this.tags, // Contiendra les balises HTML à ne pas prendre en compte
attr: this.attr // Contiendra la liste des attributs à prendre pour nom (par ordre de priorité)
};
/* RETOURNE UN OBJET CONTENANT LES DONNÉES DU FORMULAIRE
*
* @return form<Object> Objet correspondant aux données du formulaire
*
*/
FormDeflater.prototype.deflate = function(){
/* [1] On récupère tous les enfants
=========================================================*/
var children = this.getChildren( this.container );
/* [2] On filtre les éléments qui ont pas le bon tag
=========================================================*/
children = this.filterElements( children );
/* [3] On essaie de trouver les attributs primants (non vides et en premier dans la liste @this.attr)
=========================================================*/
/* (0) On initialise l'objet de retour */
var object = {};
/* (1) Pour chacun des éléments */
for( var c = 0 ; c < children.length ; c++ ){
/* (2) Pour chacun des attributs par ordre de priorité */
for( var a = 0 ; a < this.attr.length ; a++ ){
// On récupère l'attribut
var attr = children[c].getAttribute(this.attr[a]);
/* (3) Si l'attribut est défini (pas null ni vide) */
if( attr !== null && attr.length > 0 ){
/* (4) Si on a pas déja un champ de même nom */
if( object.hasOwnProperty(attr) ){
var existing = object[attr];
// {1} Si l'existant est un tableau, on ajoute notre valeur //
if( existing instanceof Array )
object[attr].push( { target: children[c], attr: this.attr[a], value: children[c].value, checked: children[c].checked } );
// {2} Sinon, si c'est une valeur seule, on crée un tableau //
else
object[attr] = [ object[attr], { target: children[c], attr: this.attr[a], value: children[c].value, checked: children[c].checked } ];
/* (5) Si c'est le premier champ avec ce nom, on le crée */
}else
object[attr] = { target: children[c], attr: this.attr[a], value: children[c].value, checked: children[c].checked };
// On en a fini pour cet élément
break;
}
}
}
/* [4] On met en forme les données
=========================================================*/
object = this.cleanOutput(object);
return object;
};
/* RETOURNE SI UN ELEMENT EST UN BOUTON DE TYPE RADIO/CHECKBOX OU NON
*
* @element<Element> Element en question
*
* @return result<Boolean> Renvoie si TRUE or FALSE il en est un
*
*/
FormDeflater.prototype.checkable = function(element){
if( element.tagName != 'INPUT' )
return false;
if( ['radio', 'checkbox'].indexOf( element.getAttribute('type').toLowerCase() ) == -1 )
return false;
return true;
};
/* NETTOIE LES DONNÉES EN SORTIE POUR QU'ELLES SOIENT UTILISABLES ET OPTIMISÉES
*
* @input<Object> Données "brutes"
*
* @return output<Object> Données sans les valeurs inutiles et explicitées
*
*/
FormDeflater.prototype.cleanOutput = function(input){
var output = {};
/* [1] On parcourt toutes les valeurs récupérées
=========================================================*/
for( var key in input ){
/* [2] Si c'est un tableau
=========================================================*/
if( input[key] instanceof Array ){
// VRAI si tous les éléments sont radio/checkbox
var areCheckable = true;
// Contiendra les indices des valeurs ou 'checked=TRUE'
var checkedIndexes = [];
/* (1) On vérifie si tous les champs sont checkables */
for( var i in input[key])
// si pas checkable, on arrête de vérifier
if( !this.checkable(input[key][i].target) ){
areCheckable = false;
break;
// Sinon si checkable et checked=TRUE, on incrémente @nbChecked
}else if( input[key][i].checked === true )
checkedIndexes.push(i);
/* (2) Si c'est que des radio ou des checkbox avec une seule valeur à TRUE */
if( areCheckable )
if( checkedIndexes.length == 1 )
output[key] = input[key][checkedIndexes[0]].value;
/* (3) Si c'est que des radio ou des checkbox avec plusieurs valeurs à TRUE */
else{
output[key] = [];
for( var i in checkedIndexes )
output[key].push( input[key][checkedIndexes[i]].value );
}
/* (4) Si c'est pas que des radio ou des checkbox, on met les valeurs */
else{
output[key] = [];
for( var i in input[key] )
output[key].push( input[key][i].value );
}
/* [3] S'il n'y a qu'une donnée (pas un tableau)
=========================================================*/
}else{
/* (1) Si de type 'radio' ou 'checkbox', on met la valeur de 'checked' */
if( this.checkable(input[key].target) )
output[key] = input[key].checked ? input[key].value : null;
/* (2) Sinon, on met la valeur de 'value' */
else
output[key] = input[key].value;
}
}
return output;
};
/* RETOURNE LA LISTE DE TOUS LES ÉLÉMENTS QUEL QUE SOIT LE NIVEAU HIÉRARCHIQUE
*
* @parent<Element> Parent duquel on veut les enfants
*
* @return children<Array> Tableau contenant tous les enfants
*
*/
FormDeflater.prototype.getChildren = function(parent){
// Si le parent n'est pas un élément, on retourne aucun enfant
if( !(parent instanceof Element) ) return [];
/* [1] Initialisation des variables
=========================================================*/
// Contient la liste des enfants directs
var children = [].slice.call(parent.children);
// Contiendra la liste des enfants directs et indirects
var allChildren = children;
/* [2] On parcourt tous les enfants
=========================================================*/
for( var i = 0 ; i < children.length ; i++ ){
// On relance la fonction récursivement sur tous les enfants
allChildren = allChildren.concat( [].slice.call(this.getChildren(children[i])) );
}
/* [3] On retourne le résultat
=========================================================*/
return allChildren;
};
/* FILTRE LES éléments en fonction de @this.tags et @this.attr
*
* @elements<Array> Le tableau contenant les éléments à trier
*
* @return filtered<Array> Retourne le tableau des éléments filtrés
*
*/
FormDeflater.prototype.filterElements = function(elements){
// Contiendra les éléments correspondants aux critères
var filtered = [];
/* [1] On parcourt tous les éléments
=========================================================*/
for( var i = 0 ; i < elements.length ; i++ )
// Si l'élément a le bon tag, on le garde
if( this.tags.indexOf( elements[i].tagName.toLowerCase() ) > -1 )
filtered.push(elements[i]);
/* [2] On retourne les éléments filtržés
=========================================================*/
return filtered;
};
/************/
/* USE CASE */
/************/
/* (1) Instanciation */
/*HIDDEN*/// var instance = new FormDeflater(
/*HIDDEN*/// document.getElementById('myform'),
/*HIDDEN*/// ['input', 'select'], // éléments à prendre en compte (tagName)
/*HIDDEN*/// ['id', 'name', 'data-elementname'] // Attributs par ordre de priorité
/*HIDDEN*/// );
/*HIDDEN*///
/* (2) On récupère l'objet */
/*HIDDEN*///
/*HIDDEN*/// var object = instance.deflate();
/*HIDDEN*///

7
js/lib/input-checker-min.js vendored Normal file
View File

@ -0,0 +1,7 @@
function formatChecker(a,c,b){this.value=a;this.pattern=c;this.rules=2<arguments.length?b:[]}
formatChecker.prototype={value:this.value,pattern:this.pattern,rules:this.rules,regexp:null,default_rules:{i:"[0-9]",a:"[a-z]",A:"[A-Z]",x:"[a-zA-Z]"},compile:function(){for(var a="^",c=[],b=0;b<this.pattern.length;b++)this.rules.hasOwnProperty(this.pattern[b])?(a+=this.rules[this.pattern[b]],c.push(this.rules[this.pattern[b]])):this.default_rules.hasOwnProperty(this.pattern[b])?(a+=this.default_rules[this.pattern[b]],c.push(this.default_rules[this.pattern[b]])):(a+=this.pattern[b],c.push(this.pattern[b]));
this.regexp=new RegExp(a+"$");this.regexp.patternDecomposition=c},check:function(a){null==this.regexp&&this.compile();if(0<arguments.length&&a instanceof Array)for(var c=0;c<this.pattern.length;c++){var b="^",b=this.rules.hasOwnProperty(this.pattern[c])?b+this.rules[this.pattern[c]]:this.default_rules.hasOwnProperty(this.pattern[c])?b+this.default_rules[this.pattern[c]]:b+this.pattern[c],b=b+"$";a.push(null!=this.value[c].match(new RegExp(b)))}return null!=this.value.match(this.regexp)}};
function inputChecker(){}
inputChecker.prototype={input:[],defval:[],checker:[],append:function(a,c,b){if(!(a instanceof HTMLInputElement&&c instanceof formatChecker))return!1;var d=this.input.push(a);if(d!=this.checker.push(c)||d!=this.defval.push(2<arguments.length?b:null))return!1},check:function(a){a=this.input.indexOf(a);if(0>a)return!1;this.checker[a].value=this.input[a].value;return this.checker[a].check()},checkAll:function(){for(var a=!0,c=0;c<this.input.length;c++)a=a&&this.check(this.input[c]);return a},correct:function(a,
c){if(0>(index=this.input.indexOf(a)))return null;c=1<arguments.length?c:!0;this.checker[index].value=this.input[index].value;this.checker[index].compile();var b=this.checker[index].regexp.patternDecomposition,d=this.input[index].value;if(!this.check(a)){for(var e=0;e<b.length&&(c||!(e>=this.input[index].value.length));e++){var f=new RegExp("^"+b[e]+"$");0==d.length||null==d[e]?d=d.slice(0,e).concat(this.defval[index][e]).concat(d.slice(e)):null==d[e].match(f)&&(d=null!=d[e].match(new RegExp("^"+
b[e+1]+"$"))?d.slice(0,e).concat(this.defval[index][e]).concat(d.slice(e)):d.slice(0,e).concat(this.defval[index][e]).concat(d.slice(e+1)))}d=d.slice(0,b.length);this.input[index].value=d}}};

View File

@ -1,8 +1,8 @@
// __ _ _ _
// / _| ___ _ __ _ __ ___ __ _| |_ ___| |__ ___ ___| | _____ _ __
// / _| ___ _ __ _ __ ___ __ _| |_ ___| |__ ___ ___| | _____ _ __
// | |_ / _ \| '__| '_ ` _ \ / _` | __|____ / __| '_ \ / _ \/ __| |/ / _ \ '__|
// | _| (_) | | | | | | | | (_| | ||_____| (__| | | | __/ (__| < __/ |
// |_| \___/|_| |_| |_| |_|\__,_|\__| \___|_| |_|\___|\___|_|\_\___|_|
// | _| (_) | | | | | | | | (_| | ||_____| (__| | | | __/ (__| < __/ |
// |_| \___/|_| |_| |_| |_|\__,_|\__| \___|_| |_|\___|\___|_|\_\___|_|
//
function formatChecker(pValue, pPattern, pRules){
this.value = pValue;
@ -64,7 +64,7 @@ formatChecker.prototype = {
/* [1] On génère la RegExp si ce n'est pas déjà fait
====================================================*/
if( this.regexp == null ) this.compile();
/* [2] Gestion de la plaque à trous
====================================================*/
@ -96,7 +96,7 @@ formatChecker.prototype = {
}
}
// on retourne TRUE si c'est bon, FALSE sinon
return this.value.match( this.regexp ) != null;
@ -131,11 +131,11 @@ formatChecker.prototype = {
// _ _ _ _
// (_)_ __ _ __ _ _| |_ ___| |__ ___ ___| | _____ _ __
// _ _ _ _
// (_)_ __ _ __ _ _| |_ ___| |__ ___ ___| | _____ _ __
// | | '_ \| '_ \| | | | __|____ / __| '_ \ / _ \/ __| |/ / _ \ '__|
// | | | | | |_) | |_| | ||_____| (__| | | | __/ (__| < __/ |
// |_|_| |_| .__/ \__,_|\__| \___|_| |_|\___|\___|_|\_\___|_|
// | | | | | |_) | |_| | ||_____| (__| | | | __/ (__| < __/ |
// |_|_| |_| .__/ \__,_|\__| \___|_| |_|\___|\___|_|\_\___|_|
//
function inputChecker(){};
@ -220,7 +220,7 @@ inputChecker.prototype = {
* @pInputElement<HTMLInputElement> l'élément <input> concerné
* @pToEnd<Boolean> si on doit corriger jusqu'à la fin ou uniquement jusqu'à l'avancéé actuelle
*
*
*
* @return correctValue<String> retourne la valeur corrigée
* retourne NULL si erreur
*/
@ -265,12 +265,12 @@ inputChecker.prototype = {
-----------------------------------------------------------*/
if( tmpValue.length == 0 || tmpValue[i] == null )
tmpValue = tmpValue.slice(0, i).concat( this.defval[index][i] ).concat( tmpValue.slice(i) );
/* (2) Valeur ne correspond pas au schéma du caractère
-----------------------------------------------------------*/
else if( tmpValue[i].match(tmpRegExp) == null ){
// si le caractère suivant match, on décale d'une position
if( tmpValue[i].match(new RegExp( '^'+RegExpByChar[i+1]+'$' )) != null )
if( tmpValue[i].match(new RegExp( '^'+RegExpByChar[i+1]+'$' )) != null )
tmpValue = tmpValue.slice(0, i).concat( this.defval[index][i] ).concat( tmpValue.slice(i) );
// sinon on remplace
else
@ -283,11 +283,11 @@ inputChecker.prototype = {
tmpValue = tmpValue.slice(0, RegExpByChar.length);
// on met à jour la valeur de l'élément input
this.input[index].value = tmpValue;
this.input[index].value = tmpValue;
}
}
};
};

9
js/lib/page-manager-min.js vendored Normal file
View File

@ -0,0 +1,9 @@
function pageManagerClass(){}var ptrPageManagerClass;
pageManagerClass.prototype={depJS:null,depCSS:null,xhr:[],activeXHR:null,page:null,vars:[],path:"",jsPath:"js",cssPath:"css",pagelist:null,container:null,ajax:function(b,c,f,a){for(var d=0;d<this.xhr.length;d++)this.xhr=this.xhr.slice(0,d-1).concat(this.xhr.slice(d,this.xhr.length-1));var e;e=window.XMLHttpRequest?this.xhr.push(new XMLHttpRequest)-1:this.xhr.push(new ActiveXObject("Microsoft.XMLHttpRequest"))-1;this.activeXHR=this.xhr[e];var g=this;this.xhr[e].onreadystatechange=function(){4==g.xhr[e].readyState&&
(-1<[0,200].indexOf(g.xhr[e].status)?c(g.xhr[e].responseText):c())};f="string"==typeof f&&/^POST|GET$/i.test(f)?f.toUpperCase():"POST";a="POST"==f&&"object"==typeof a&&a instanceof FormData?a:null;this.xhr[e].open(f,b,!0);this.xhr[e].send(a);return this},explodeURL:function(b){b=1<=arguments.length?b:document.URL;if(null!=this.pagelist&&/^(?:(?:https?:\/\/)?[^\/]+)\/([a-z0-9_]+)\/?(?:\/((?:.+\/)+)\/?)?$/i.test(b)){for(var c=RegExp.$2.split("/");""==c[c.length-1];)c.pop();return-1<this.pagelist.indexOf(RegExp.$1)?
{page:RegExp.$1,"var":c}:null}return null},loadDependencies:function(){"object"==typeof this.depCSS&&this.depCSS instanceof Element&&this.depCSS.parentNode==document.head&&document.head.removeChild(this.depCSS);"object"==typeof this.depJS&&this.depJS instanceof Element&&this.depJS.parentNode==document.head&&document.head.removeChild(this.depJS);ptrPageManagerClass=this;this.ajax(this.path+"/"+this.cssPath+"/"+this.page+".css",function(b){null!=b?(ptrPageManagerClass.depCSS=document.createElement("link"),
ptrPageManagerClass.depCSS.rel="stylesheet",ptrPageManagerClass.depCSS.type="text/css",ptrPageManagerClass.depCSS.href=ptrPageManagerClass.path+"/"+ptrPageManagerClass.cssPath+"/"+ptrPageManagerClass.page+".css",document.head.appendChild(ptrPageManagerClass.depCSS)):console.warn("[loadDependencies_Error] - ("+ptrPageManagerClass.path+"/"+ptrPageManagerClass.cssPath+"/"+ptrPageManagerClass.page+".css)")});this.ajax(this.path+"/"+this.jsPath+"/"+this.page+".js",function(b){null!=b?(ptrPageManagerClass.depJS=
document.createElement("script"),ptrPageManagerClass.depJS.type="text/javascript",ptrPageManagerClass.depJS.src=ptrPageManagerClass.path+"/"+ptrPageManagerClass.jsPath+"/"+ptrPageManagerClass.page+".js",document.head.appendChild(ptrPageManagerClass.depJS)):console.warn("[loadDependencies_Error] - ("+ptrPageManagerClass.path+"/"+ptrPageManagerClass.jsPath+"/"+ptrPageManagerClass.page+".js)")})},updateURL:function(){0<this.vars.length?window.history.pushState(this.page,this.page,"/"+this.page+"/"+this.vars.join("/")+
"/"):window.history.pushState(this.page,this.page,"/"+this.page+"/")},setPage:function(b,c,f,a){var d="object"==typeof a&&a instanceof Array?a:null;if(null!=d)for(a=0;a<d.length&&(d="string"==typeof d[a]&&/^[a-z0-9_]+$/i.test(d[a])?d:null,null!=d);a++);this.pagelist=null!=d?d:this.pagelist;this.page=this.pagelist[0];this.path="string"==typeof c?c:this.path;this.container="object"==typeof f&&f instanceof Element?f:this.container;if(null!=this.pagelist&&null!=this.container)if("string"==typeof b&&-1<
this.pagelist.indexOf(b)){this.page=b;var e=this;b=new FormData;for(a=0;a<this.vars.length;a++)b.append(this.vars[a],null);this.ajax(this.path+"/"+this.page+".php",function(a){e.container.innerHTML=a;e.loadDependencies()},"POST",b);this.updateURL()}else if(c=this.explodeURL(),null!=c){this.page=c.page;var g=this;b=new FormData;for(a=this.vars.length=0;a<c["var"].length;a++)this.vars[a]=c["var"][a],b.append(this.vars[a],null);this.ajax(this.path+"/"+this.page+".php",function(a){g.container.innerHTML=
a;g.loadDependencies()},"POST",b);this.updateURL()}else this.setPage(this.pagelist[0]);else console.warn("pagelist et container manquant");return this},refresh:function(){this.setPage(this.page);return this}};

View File

@ -45,7 +45,7 @@ pageManagerClass.prototype = {
if(window.XMLHttpRequest) // IE7+, Firefox, Chrome, Opera, Safari
index = this.xhr.push( new XMLHttpRequest() ) -1;
else // IE5, IE6
else // IE5, IE6
index = this.xhr.push( new ActiveXObject('Microsoft.XMLHttpRequest') ) -1;
// On definit un pointeur sur l'instance XHR active (ajax)
@ -83,7 +83,7 @@ pageManagerClass.prototype = {
// var fd = new FormData(); // création d'un formulaire
// fd.append('var', 100); // ajout de la variable VAR qui vaut 100
// ajax( 'index.php', alert, null, fd ); // saut de paramètre avec null + envoi formulaire
// ajax( 'index.php?var=10', alert, 'GET' ); // envoi formulaire en GET (dans l'url)
// ajax( 'index.php?var=10', alert, 'POST', fd ); // envoi formulaire en GET (dans l'url) + en POST via le formulaire FD
@ -101,7 +101,7 @@ pageManagerClass.prototype = {
explodeURL: function(url_data){
url_data = (arguments.length >= 1) ? url_data : document.URL;
// si pageList est correct et que l'URL correspond à un schéma de page => continue [sinon] return null
if( this.pagelist != null && /^(?:(?:http:\/\/)?[^\/]+)\/([a-z0-9_]+)\/?(?:\/((?:.+\/)+)\/?)?$/i.test(url_data) ){
if( this.pagelist != null && /^(?:(?:https?:\/\/)?[^\/]+)\/([a-z0-9_]+)\/?(?:\/((?:.+\/)+)\/?)?$/i.test(url_data) ){
// si la page récupérée dans l'url est dans la liste => renvoi de l'objet [sinon] null
var vars = RegExp.$2.split('/');
while( vars[vars.length-1] == '' ) // on supprime les dernières entrées vides
@ -156,7 +156,7 @@ pageManagerClass.prototype = {
},
/* =======================================================================
Met à jour l'URL de la page en fonction de la page chargée et des
Met à jour l'URL de la page en fonction de la page chargée et des
variables associées (ne recharge aucune ressource)
======================================================================= */
updateURL: function(){
@ -177,13 +177,13 @@ pageManagerClass.prototype = {
- pContainer<Element> l'élément du DOM qui contiendra la page chargée (**)
- pPageList<Array<string>> tableau contenant la liste des pages sous forme de chaînes de caractères (**) (***)
* Le chemin du dossier sans le '/' final si c'est le dossier actuel le chemin est une chaîne vide
Si le dossier est 'page' et que l'on cherche la page 'accUe1l', la requête sera vers 'page/accUe1l.php'
le nom de la page est sensible à la casse
Si le dossier est 'page' et que l'on cherche la page 'accUe1l', la requête sera vers 'page/accUe1l.php'
le nom de la page est sensible à la casse
** 1. pPageList et pContainer doivent être mis en paramètres uniquement à la première utilisation
et la première utilisation doit se faire au chargement de la page car elle permetra
de mettre l'URL à jour et/ou charger la page de l'URL
et la première utilisation doit se faire au chargement de la page car elle permetra
de mettre l'URL à jour et/ou charger la page de l'URL
*** la première page du tableau est la page par défaut (qui est chargée si l'URL ne contient
pas la page ou si la page de l'URL ne correspond à aucune page de la liste)
pas la page ou si la page de l'URL ne correspond à aucune page de la liste)
========================================================================== */
setPage: function(pName, pPath, pContainer, pPageList){
@ -205,7 +205,7 @@ pageManagerClass.prototype = {
/* on attribue le paramètre pContainer à l'attribut si il est spécifié */
this.container = ( typeof pContainer == 'object' && pContainer instanceof Element ) ? pContainer : this.container;
// si this.pagelist && this.container ne sont pas null &&
// si this.pagelist && this.container ne sont pas null &&
if( this.pagelist != null && this.container != null ){
// si le pName est renseigné et qu'il est dans pagelist
if( typeof pName == 'string' && this.pagelist.indexOf(pName) > -1 ){
@ -219,15 +219,15 @@ pageManagerClass.prototype = {
var fd = new FormData();
for( var i = 0 ; i < this.vars.length ; i++ )
fd.append(this.vars[i], null);
this.ajax(this.path+'/'+this.page+'.php', function(e){
ptrPageManagerClass.container.innerHTML = e;
ptrPageManagerClass.loadDependencies();
ptrPageManagerClass.loadDependencies();
}, 'POST', fd);
// change l'URL en conséquences(stateObj, titre, url)
this.updateURL();
}else{ // si la page n'est pas spécifiée ou qu'elle n'est pas dans la liste des pages
var urlGet = this.explodeURL();
@ -248,12 +248,12 @@ pageManagerClass.prototype = {
this.ajax(this.path+'/'+this.page+'.php', function(e){
ptrThis.container.innerHTML = e;
ptrThis.loadDependencies();
ptrThis.loadDependencies();
}, 'POST', fd);
// change l'URL en conséquences(stateObj, titre, url)
this.updateURL();
}else // si l'url ne contient rien, on charge la page par défaut
this.setPage(this.pagelist[0]);
}
@ -276,4 +276,4 @@ pageManagerClass.prototype = {
return this;
}
}
}

2
js/lib/reset-min.js vendored Normal file
View File

@ -0,0 +1,2 @@
function $(a){var b=document.querySelectorAll("#"+a);a=document.querySelectorAll("."+a);return 0<b.length?b[0]:a[0]}Element.prototype.getData=function(a){return"undefined"==typeof this.dataset?!1:this.dataset.hasOwnProperty(a)?this.dataset[a]:!1};Element.prototype.addClass=function(a){var b=this.className.split(" ");-1<b.indexOf(a)||(b.push(a),this.className=b.join(" ").trim())};
Element.prototype.remClass=function(a){var b=this.className.split(" ");a=b.indexOf(a);-1!=a&&(b=b.slice(0,a).concat(b.slice(a+1)),this.className=b.join(" ").trim())};NodeList.prototype.indexOf=HTMLCollection.prototype.indexOf=function(a){for(var b=0;b<this.length;b++)if(this[b]==a)return b;return-1};Element.prototype.anim=function(a,b){var c=this;c.addClass(a);setTimeout(function(){c.remClass(a)},b)};var format_code=new formatChecker(null,"HH-HH-HH-HH",{H:"[0-9A-F]"});

View File

@ -110,4 +110,4 @@ Element.prototype.anim = function(className, timeout){
/* DEFINITION DES FORMATS UTILES POUR INPUT-CHECKER
*
*/
var format_code = new formatChecker(null, 'HH-HH-HH-HH', { 'H' : '[0-9A-F]'} )
var format_code = new formatChecker(null, 'HH-HH-HH-HH', { 'H' : '[0-9A-F]'} )

View File

@ -1,10 +1,10 @@
<?php
namespace manager;
class DataBase{
/* ATTRIBUTS STATIQUES */
public static $config_path = array(
'local' => 'f/json/database-local/conf',
@ -29,8 +29,8 @@
$this->dbname = $dbname;
$this->username = $username;
$this->password = $password;
try{
try{
self::$pdo = new \PDO('mysql:host='.$this->host.';dbname='.$this->dbname, $this->username, $this->password);
// On signale que tout s'est bien passe
@ -48,7 +48,7 @@
if( self::$instance == null || self::$error != ManagerError::Success ){ // Si aucune instance existante OU erreur de connection
// chargement de la configuration du server SQL
if( !isset($_SERVER['HTTP_HOST']) || isset($_SERVER['HTTP_HOST']) && $_SERVER['HTTP_HOST'] == 'stefproject' )
if( !checkdnsrr($_SERVER['SERVER_NAME'], 'NS') )
$conf = json_decode( ResourceDispatcher::getResource(self::$config_path['local']), true );
else
$conf = json_decode( ResourceDispatcher::getResource(self::$config_path['remote']), true );
@ -57,7 +57,7 @@
self::$instance = new DataBase($conf['host'], $conf['dbname'], $conf['user'], $conf['password']);
}
return self::$instance;
}
@ -104,7 +104,7 @@
* @fetchData<Array> le résultat d'une $requeteSQL->fetchAll()
* @oneDimension<Boolean> FAUX <=> fetchAll ; VRAI <=> fetch
*
* @return newFetchData<Array> retourne le tableau donné en paramètre mais sans les valeurs à clés numériques
* @return newFetchData<Array> retourne le tableau donné en paramètre mais sans les valeurs à clés numériques
*
*/
public static function delNumeric($fetchData, $oneDimension=false){
@ -121,19 +121,19 @@
// on supprime les doublons des entrées (indice numérique)
for( $i = 0 ; $i < count($fetchData) ; $i++ ) // pour tout les utilisateurs
foreach($fetchData[$i] as $col => $val){ // pour toutes les entrées
if( !mb_detect_encoding($val, 'UTF-8') )
if( !\mb_detect_encoding($val, 'UTF-8') )
$fetchData[$i][$col] = utf8_encode($val);
if( is_int($col) ){ // Si indice numerique
if( $nextEquivalent ) // Si suit un indice textuel
unset( $fetchData[$i][$col] ); // on supprime l'indice
$nextEquivalent = false; // Dans tous les cas, on dit que le prochain ne pourra pas etre supprime si numerique
}else // Si l'indice n'est pas un entier
$nextEquivalent = true; // On signale qu'il y aura peut etre un indice numerique suivant
}
/* [2] 1 dimensions
@ -142,14 +142,14 @@
// on supprime les doublons des entrées (indice numérique)
foreach($fetchData as $i=>$val){ // pour toutes les entrées
if( !mb_detect_encoding($val, 'UTF-8') )
if( !\mb_detect_encoding($val, 'UTF-8') )
$fetchData[$i] = utf8_encode($val);
if( is_int($i) ){ // Si indice numerique
if( $nextEquivalent ) // Si suit un indice textuel
unset( $fetchData[$i] ); // on supprime l'indice
$nextEquivalent = false; // Dans tous les cas, on dit que le prochain ne pourra pas etre supprime si numerique
}else // Si l'indice n'est pas un entier
@ -165,12 +165,12 @@
////////////////////////////////////////////////////////////////
// _ __ _ _ _
// __ _____ _ __(_)/ _(_) ___ __ _| |_(_) ___ _ __ ___
// \ \ / / _ \ '__| | |_| |/ __/ _` | __| |/ _ \| '_ \/ __|
// \ V / __/ | | | _| | (_| (_| | |_| | (_) | | | \__ \
// \_/ \___|_| |_|_| |_|\___\__,_|\__|_|\___/|_| |_|___/
//
// _ __ _ _ _
// __ _____ _ __(_)/ _(_) ___ __ _| |_(_) ___ _ __ ___
// \ \ / / _ \ '__| | |_| |/ __/ _` | __| |/ _ \| '_ \/ __|
// \ V / __/ | | | _| | (_| (_| | |_| | (_) | | | \__ \
// \_/ \___|_| |_|_| |_|\___\__,_|\__|_|\___/|_| |_|___/
//
////////////////////////////////////////////////////////////////
@ -183,43 +183,104 @@
*
*/
public static function check($type, $value){
$checker = !is_null($value);
$checker = true;
/* [0] On verifie que $value n'est pas nul
=========================================================*/
if( is_null($value) ) return false;
/* [1] Si de type VARCHAR(min, max)
=========================================================*/
if( preg_match('/^varchar\((\d+), ?(\d+)\)$/', $type, $match) ){
// On recupere la taille min
$min = (int) $match[1];
// On recupere la taille max
$max = (int) $match[2];
// On effectue la verification
return $checker && is_string($value) && strlen($value) <= $max && strlen($value) >= $min;
}
/* [2] Si de type ARRAY(type_elements)
=========================================================*/
if( preg_match('/^array<(.+)>$/', $type, $match) ){
// Si c'est pas un tableau on retourne une erreur
if( !is_array($value) )
return false;
$elements_type = $match[1];
// On verifie le type pour chaque element
foreach($value as $element)
// Si erreur dans au moins 1 element, on retourne que c'est incorrect
if( !self::check($elements_type, $element) )
return false;
// Si aucune erreur, on retourne que tout est bon
return true;
}
/* [n] Sinon, tous les autres types definis
=========================================================*/
switch($type){
/* (1) Global */
case 'auto_increment_id':
return $checker && is_numeric($value) && $value <= 2147483647 && $value >= -2147483647;
// Quoi que ce soit
case 'mixed':
return $checker && !is_null($value);
break;
/* (2) Utilisateur */
case 'user.code':
case 'machine.code':
return $checker && is_string($value) && preg_match('/^[\dA-F]{2}(\-[\dA-F]{2}){3,5}$/i', $value);
// Entier positif (id dans BDD)
case 'id':
return $checker && is_numeric($value) && $value <= 2147483647 && $value >= 0;
break;
case 'user.username':
case 'machine.name':
case 'group.name':
return $checker && is_string($value) && preg_match('/^[\w-]{1,30}$/i', $value);
break;
case 'user.firstname':
case 'user.lastname':
return $checker && is_string($value) && preg_match('/^[a-z -]{3,30}$/i', $value);
break;
case 'user.mail':
// String quelconque (peut etre vide)
case 'text':
return $checker && is_string($value);
// Adresse mail (255 caracteres max)
case 'mail':
return $checker && is_string($value) && strlen($value) <= 50 && preg_match('/^[\w\.-]+@[\w\.-]+\.[a-z]{2,4}$/i', $value);
break;
case 'user.password':
// Hash sha1
case 'sha1':
return $checker && is_string($value) && preg_match('/^[\da-f]{40}$/i', $value);
break;
case 'user.status':
return $checker && is_numeric($value) && floor($value) == $value && $value >= 0 && $value <= 100;
// Numéro de téléphone
case 'number':
return $checker && is_string($value) && preg_match('/^(?:0|\+33 ?|0?0?33 ?|)([1-9] ?(?:[0-9] ?){8})$/i', $value);
break;
// Tableau non vide
case 'array':
return $checker && is_array($value) && count($value) > 0;
break;
// Boolean
case 'boolean':
return $checker && is_bool($value);
break;
// Objet non vide
case 'object':
return $checker && is_object($value) && count((array) $value) > 0;
break;
// Chaine JSON (on vérifie via le parser)
case 'json':
return $checker && is_string($value) && json_decode($value, true) !== NULL;
break;
default:
return false;
break;
}
return $checker;
@ -227,5 +288,80 @@
}
/* FONCTION QUI FORMATTE UN NUMÉRO DE TÉLÉPHONE
*
* @number<String> Numéro de téléphone en +336/336/06/0336/00336
*
* @return formatted<String> Numéro formatté (06), on FALSE si erreur
*
*/
public static function formatNumber($number){
// On met en <string> quel que soit le type
$number = (string) $number;
// On supprime tous les espaces
$number = str_replace(' ', '', $number);
// On formatte le numéro
if( preg_match("/^(?:\+33|0?0?33|0)(.+)/", $number, $m) )
$number = '0'.$m[1];
// On retourne le numéro formatté
return $number;
}
public static function readableNumber($number){
/* (1) On formatte le numéro si c'est pas fait */
$formatted = self::formatNumber($number);
for( $i = 1 ; $i < strlen($formatted) ; $i++ )
if( ($i-2) % 3 == 0 )
$formatted = substr($formatted, 0, $i).' '.substr($formatted, $i);
return $formatted;
}
////////////////////////////////////
// _ _
// __| | __ _| |_ ___ ___
// / _` |/ _` | __/ _ \/ __|
// | (_| | (_| | || __/\__ \
// \__,_|\__,_|\__\___||___/
//
////////////////////////////////////
// 1) Convertis une date en en francais explicite
public static function frDate($date){
/* [1] On definit les traductions
=========================================================*/
// Jours de la semaine
$days = array("Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi", "Dimanche");
// Mois de l'annee
$months = array("Janvier", "Février", "Mars", "Avril", "Mai", "Juin", "Juillet", "Août", "Septembre", "Octobre", "Novembre", "Décembre");
/* [2] On recupere le timestamp et les indices
=========================================================*/
$time = strtotime($date); // timestamp
$daynum = intval( date('N', $time)-1 ); // jour dans la semaine
$monthnum = intval( date('n', $time)-1 ); // numero du mois dans l'annee
/* [3] On recupere les infos independemment
=========================================================*/
$result = array(
$days[$daynum], // nom de jour
date('j', $time), // jour du mois
$months[$monthnum], // nom du mois
date('Y', $time), // annee
);
return implode(" ", $result);
}
}
?>

View File

@ -1,6 +1,6 @@
<?php
namespace manager;
@ -60,6 +60,21 @@
// Erreur lors de la creation d'un objet PDO (connection)
const PDOConnection = 14;
/* API token */
// Token inexistant ou faux
const TokenError = 15;
const PermissionError = 16;
/* Erreur d'UPLOAD */
const UploadError = 17;
// Mauvais format de fichier
const FormatError = 18;
/* Erreur au niveau javascript */
//const JavascriptError = 19; // -> géré en js
/* EXPLICITE UN CODE D'ERREUR
*
@ -70,33 +85,43 @@
*/
public static function explicit($error){
switch($error){
case self::Success: return "Tout s'est bien deroule"; break;
case self::Success: return "Tout s'est bien deroulé."; break;
case self::ParsingFailed: return "La lecture du fichier JSON a echoue"; break;
case self::InvalidFlags: return "Les specifications (drapeaux) sont incorrects"; break;
case self::UnreachableResource: return "La ressource n'existe pas (404)"; break;
case self::MissingPath: return "Le chemin de delegation n'a pas ete renseigne"; break;
case self::WrongPathModule: return "Le chemin de delegation est incorrect ('nomModule/nomMethode')"; break;
case self::WrongPathRepo: return "Le chemin de delegation est incorrect ('nomRepo/nomMethode')"; break;
case self::UnknownModule: return "Le module n'existe pas"; break;
case self::UnknownRepo: return "Le repo n'existe pas"; break;
case self::UnknownMethod: return "Le methode n'existe pas"; break;
case self::UncallableMethod: return "Le methode n'est pas amorcable"; break;
case self::ParsingFailed: return "La lecture du fichier JSON ou XML a echouée."; break;
case self::ParamError: return "Un ou plusieurs parametres sont manquants ou incorrects"; break;
case self::ModuleError: return "Erreur lors du traitement du module"; break;
case self::RepoError: return "Erreur lors du traitement du repo"; break;
case self::InvalidFlags: return "Les spécifications (drapeaux) sont incorrects."; break;
case self::UnreachableResource: return "La ressource n'existe pas (404)."; break;
case self::MissingPath: return "Le chemin de délégation n'a pas été renseigné."; break;
case self::WrongPathModule: return "Le chemin de délégation est incorrect ('nomModule/nomMethode')."; break;
case self::WrongPathRepo: return "Le chemin de délégation est incorrect ('nomRepo/nomMethode')."; break;
case self::UnknownModule: return "Le module n'existe pas."; break;
case self::UnknownRepo: return "Le repo n'existe pas."; break;
case self::UnknownMethod: return "Le methode n'existe pas."; break;
case self::UncallableMethod: return "Le methode n'est pas amorçable."; break;
case self::PDOConnection: return "La connexion avec la base de donnees a echoue"; break;
case self::ParamError: return "Un ou plusieurs paramètres sont manquants ou incorrects."; break;
case self::ModuleError: return "Erreur lors du traitement du module."; break;
case self::RepoError: return "Erreur lors du traitement du repo."; break;
// default: return "Erreur inconnue..."; break;
case self::PDOConnection: return "La connexion avec la base de données a echouée."; break;
case self::TokenError: return "Le token de connection est absent, érroné ou expiré."; break;
case self::PermissionError: return "Vous n'avez pas la permission d'effectuer cette action."; break;
case self::UploadError: return "Une erreur d'upload est survenue."; break;
case self::FormatError: return "Le fichier n'est pas au bon format."; break;
default: return "Description d'erreur inconnue..."; break;
}
// Erreur inconnue
return null;
}
public static function setHttpCode($error){
http_response_code( $error == self::Success ? 200 : 417 );
}
}
?>
?>

View File

@ -1,28 +1,22 @@
<?php
<?php
namespace manager;
use \manager\Database;
// FORMAT:
//
// path: "nomModule/nomMethode"
// data1: {donnee1}
// data2: {donnee2}
// ...
//
//
//
//
class ModuleRequest{
// Constantes
public static $config_path = 'f/json/modules/conf';
public static $default_options = array(
'download' => false
);
// Attributs prives utiles (initialisation)
private $path;
private $data;
private $params;
private $modules;
private $options;
// Contiendra la reponse a la requete
public $answer;
@ -37,12 +31,13 @@
/* CONSTRUCTEUR D'UNE REQUETE DE MODULE
*
* @path<String> Chemin de delegation ("module/methode")
* @data<Array> Tableau contenant les parametres utiles au traitement
* @param<Array> Tableau associatif contenant les parametres utiles au traitement
* @token<String> Token d'acces a l'api (OPTIONNEL)
*
* @return status<Boolean> Retourne si oui ou non tout s'est bien passe
*
*/
public function __construct($path=null, $data=null){
public function __construct($path=null, $params=null, $token=null){
// Si pas parametre manquant, on quitte
if( $path == null ){
$this->error = ManagerError::MissingPath;
@ -53,7 +48,7 @@
=========================================================*/
// Modules specifies
$this->modules = json_decode( ResourceDispatcher::getResource(self::$config_path), true );
// Gestion de l'erreur de parsage
if( $this->modules == null ){
$this->error = ManagerError::ParsingFailed;
@ -71,19 +66,37 @@
}
// Type de @data (optionnel)
$data = (is_array($data)) ? $data : array();
$params = (is_array($params)) ? $params : array();
/* [2] Verification du chemin (existence module+methode)
=========================================================*/
if( !$this->checkPath($path) ) // Verification de la coherence du chemin + attribution
return false;
// Gestion d'erreur interne
/* [3] Construction de l'objet
/* [3] Verification des droits
=========================================================*/
$this->data = $data;
if( !$this->checkPermission($token) ) // Si on a pas les droits
return false;
/* [4] Verification des parametres (si @type est defini)
=========================================================*/
if( !$this->checkParams($params) ){ // Verification de tous les types
$this->error = ManagerError::ParamError;
return false;
}
/* [5] Récupèration des options
=========================================================*/
$this->buildOptions();
/* [6] Construction de l'objet
=========================================================*/
$this->params = $params;
$this->error = ManagerError::Success;
return true; // On retourne que tout s'est bien passe
@ -94,35 +107,40 @@
/* EXECUTE LE TRAITEMENT ASSOCIE ET REMPLIE LA REPONSE
*
* @return answer<ModuleAnswer> Retourne une reponse de type <ModuleAnswer> si tout s'est bien passe
* @return answer<ModuleResponse> Retourne une reponse de type <ModuleResponse> si tout s'est bien passe
*
*/
public function dispatch(){
/* [0] Si c'est un download, on lance la methode `download()`
=========================================================*/
if( $this->options['download'] === true )
return $this->download();
/* [1] On verifie qu'aucune erreur n'a ete signalee
=========================================================*/
if( $this->error != ManagerError::Success ) // si il y a une erreur
return new ModuleAnswer($this->error); // on la passe a la reponse
return new ModuleResponse($this->error); // on la passe a la reponse
/* [2] On verifie que la methode est amorcable
=========================================================*/
if( !is_callable($this->getFunctionCaller()) ){
$this->error = ManagerError::UncallableMethod;
return new ModuleAnswer($this->error);
return new ModuleResponse($this->error);
}
/* [3] On amorce la methode
=========================================================*/
$returned = call_user_func_array( $this->getFunctionCaller(), $this->data );
$returned = call_user_func( $this->getFunctionCaller(), $this->params );
/* [4] Gestion de la reponse
=========================================================*/
$answer = new ModuleAnswer($this->error);
$answer->appendAll($returned);
$response = new ModuleResponse($this->error);
$response->appendAll($returned);
return $answer;
return $response;
}
@ -130,71 +148,168 @@
/* DESERIALISATION ET CREATION D'UN OBJET
*
* @jsonString<String> Json au format string contenant les donnees
*
* @return instance<ModuleRequest> Retourne un objet de type <ModuleRequest>
/* EXECUTE LE TRAITEMENT ASSOCIE ET RENVOIE UN FICHIER AVEC LE HEADER ET LE BODY SPECIFIE
*
*/
public static function fromString($jsonString){
$json = json_decode( $jsonString, true );
public function download(){
/* [1] On verifie qu'aucune erreur n'a ete signalee
=========================================================*/
if( $this->error != ManagerError::Success ) // si il y a une erreur
return new ModuleResponse($this->error); // on la passe a la reponse
// Verification du parsage
if( $json == null )
return new ModuleRequest();
// Verification des parametres
if( !isset($json['path']) )
return new ModuleRequest();
/* [2] On verifie que la methode est amorcable
=========================================================*/
if( !is_callable($this->getFunctionCaller()) ){
$this->error = ManagerError::UncallableMethod;
return new ModuleResponse($this->error);
}
// On definit $data au cas ou il soit vide
$data = (isset($json['data'])) ? $json['data'] : array();
return new ModuleRequest($json['path'], $data);
/* [3] On amorce la methode
=========================================================*/
$returned = call_user_func( $this->getFunctionCaller(), $this->params );
/* [4] Vérification des erreurs et paramètres
=========================================================*/
/* (1) Vérification de l'erreur retournée, si pas Success, on retourne l'erreur */
if( isset($returned['ModuleError']) && $returned['ModuleError'] != ManagerError::Success ){
$this->error = $returned['ModuleError'];
return new ModuleResponse($this->error);
}
/* (2) Vérification du contenu, si pas défini */
if( !isset($returned['body']) ){
$this->error = ManagerError::ParamError;
return new ModuleResponse($this->error);
}
/* (3) Si @headers n'est pas défini on met par défaut */
if( !isset($returned['headers']) || !is_array($returned['headers']) )
$returned['headers'] = array();
$fromAjax = isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest';
/* [5] Si la requête vient d'ajax on crée un fichier temporaire et on renvoie son URL
=========================================================*/
if( $fromAjax ){
$tmpfname = '/tmp/download_'.uniqid().'.php';
$bodyfname = __ROOT__.'/tmp/content_'.uniqid().'.php';
/* (1) On crée le fichier temporaire */
$tmpfnameroot = __ROOT__.$tmpfname;
$tmpfile = fopen($tmpfnameroot, 'w');
fwrite($tmpfile, '<?php'.PHP_EOL);
/* (2) Script qui écrira les headers */
foreach($returned['headers'] as $header=>$value)
fwrite($tmpfile, "header(\"$header: $value\");".PHP_EOL);
/* (3) Script qui écrira le contenu */
// 1) On écrit le contenu dans un fichier temporaire (et oui encore)
$bodyfile = fopen($bodyfname, 'w');
fwrite($bodyfile, $returned['body']);
fclose($bodyfile);
chmod($bodyfname, 0775);
fwrite($tmpfile, "readfile('$bodyfname');".PHP_EOL);
/* (4) Script qui supprimera les fichiers temporaires */
fwrite($tmpfile, "unlink('$bodyfname');".PHP_EOL);
fwrite($tmpfile, "unlink(__FILE__);".PHP_EOL);
fwrite($tmpfile, '?>'.PHP_EOL);
/* (5) On ferme le fichier */
fclose($tmpfile);
chmod($tmpfnameroot, 0775);
$response = new ModuleResponse(ManagerError::Success);
$response->append('link', $tmpfname);
return $response;
/* [6] Gestion du download direct si pas AJAX
=========================================================*/
}else{
/* (1) On définit les headers */
foreach($returned['headers'] as $header=>$value)
header($header.': '.$value);
/* (2) On affiche le contenu */
echo $returned['body'];
return true;
}
}
/* DESERIALISATION A PARTIR DES DONNEES POST
*
* @url<String> Contenu de l'url après api/ (si existe)
* @post<Array> Tableau des donnes $_POST => @path + @data (opt)
*
* @return instance<ModuleRequest> Retourne un objet de type <ModuleRequest>
*
*/
public static function fromPost($post){
public static function fromPost($url, $post){
/* [0] Verification de l'authentification
=========================================================*/
// On definit le token
$token = isset($_SERVER['PHP_AUTH_DIGEST']) ? $_SERVER['PHP_AUTH_DIGEST'] : null;
/* [1] On verifie que le @path est renseigne
=========================================================*/
/* (1) Si le path est dans @url */
$pathInUrl = is_string($url[0]) && strlen($url[0]) > 0 && preg_match('#^([\w_-]+)/([\w_-]+)/?$#', $url[0], $urlMatches);
// On l'utilise pour le chemin
if( $pathInUrl )
$post['path'] = $urlMatches[1].'/'.$urlMatches[2];
/* (2) On vérifie dans tous les cas si le path existe */
if( !isset($post['path']) )
return new ModuleRequest();
/* [2] On verifie que @data est renseigne
=========================================================*/
// Si variable n'existe pas, on cree un tableau vide
$data = (isset($post['data'])) ? $post['data'] : array();
$params = $post;
// Si c'est toujours pas un tableau, on essaie de voir si c'est un json
$data = (!is_array($data)) ? json_decode($data, true) : $data;
// Si toujours pas de tableau, on cree un tableau vide
$data = (!is_array($data)) ? array() : $data;
// On retire le @path de @params
unset($params['path']);
/* [3] On retourne une instance de <ModuleRequest>
/* [3] On met les paramètres JSON en JSON (si ils décodent sans erreur)
=========================================================*/
return new ModuleRequest($post['path'], $data);
foreach($params as $name=>$value){
$json = json_decode( $value, true );
// Si aucune erreur, on affecte la valeur
if( !is_null($json) )
$params[$name] = $json;
}
/* [4] On retourne une instance de <ModuleRequest>
=========================================================*/
// On cree notre requete avec le token
return new ModuleRequest($post['path'], $params, $token);
}
/* VERIFICATION DU FORMAT ET DE LA COHERENCE DU CHEMIN SPECIFIE
*
* @path<String> String correspondant au chemin de delegation ("module/methode")
@ -214,6 +329,7 @@
$module = $matches[1];
$method = $matches[2];
/* [2] Verification de l'existence du module (conf)
=========================================================*/
if( !array_key_exists($module, $this->modules) ){ // Si le module n'est pas specifie dans la conf
@ -223,11 +339,11 @@
/* [3] Verification de l'existence de la methode (conf)
=========================================================*/
if( array_search($method, $this->modules[$module]) === false ){ // Si la methode n'est pas specifie dans la conf
if( array_key_exists($method, $this->modules[$module]) === false ){ // Si la methode n'est pas specifie dans la conf
$this->error = ManagerError::UnknownMethod;
return false; // On retourne FALSE, si erreur
}
/* [4] Enregistrement du chemin et renvoi de SUCCESS
@ -244,6 +360,180 @@
/* RETOURNE SI ON A LA PERMISSION D'EXECUTER CETTE METHODE
*
* @token<String> Token d'acces a l'API (OPTIONNEL)
*
* @return permission<bool> Retourne si on a les droits ou pas pour executer cette methode
*
*/
private function checkPermission($token=null){
/* [1] On recupere les informations utiles
=========================================================*/
// On recupere le nom de la methode
$method = $this->modules[$this->path['module']][$this->path['method']];
// Si aucune permission n'est definie
if( !isset($method['permissions']) ) return true;
/* [2] Gestion si un @token est defini
=========================================================*/
if( Database::check('sha1', $token) ){
/* (1) On verifie que le token est valide */
$checkToken = new Repo('token/check', array($token) );
$token_permissions = $checkToken->answer();
// Si le token est invalide, on retourne une erreur -> FAUX
if( $token_permissions === false ){
$this->error = ManagerError::TokenError;
return false;
}
$local_permissions = $token_permissions;
/* [3] Gestion si aucun token, avec utilisateur connecté
=========================================================*/
}else if( isset($_SESSION['permission']) )
$local_permissions = $_SESSION['permission'];
// Si ni token, ni SESSION, erreur
else{
$this->error = ManagerError::PermissionError;
return false;
}
/* [4] Verification des droits parmi les permissions donnees
=========================================================*/
/* (1) On recupere la liste des permissions possibles */
$permissions = $method['permissions'];
/* (2) Si aucune permission n'est definie, on laisse l'acces */
if( count($permissions) == 0 ) return true;
/* (3) On verifie qu'il y a au moins une permission ok */
foreach($permissions as $permission)
if( in_array($permission, $local_permissions) ) return true;
/* [5] On retourne FAUX si aucun droit n'a ete trouve
=========================================================*/
$this->error = ManagerError::PermissionError;
return false;
}
/* VERIFICATION DU TYPE DES PARAMETRES ENVOYES
*
* @params<Array> Tableau associatif contenant les parametres
* @params peut se voir rajouter les paramètres optionnels s'ils ne sont pas renseignés (initialisés à NULL)
*
* @return correct<bool> Retourne si oui ou non les parametres ont le bon type
*
*/
private function checkParams(&$params){
/* [1] On verifie qu'il ne manque aucun parametre
=========================================================*/
// Si @params n'est pas un tableau
if( !is_array($params) ) return false;
$method = $this->modules[$this->path['module']][$this->path['method']];
/* [2] Si le type est defini, pour chaque param, on teste
=========================================================*/
foreach($method['parameters'] as $name=>$paramsdata){
/* (1) On récupère si le paramètre est optionnel ou pas */
$optional = isset($paramsdata['optional']) && $paramsdata['optional'] === true;
/* (2) Récupère si le paramètre est un fichier et définit comme de type 'FILE' */
$isFile = isset($paramsdata['type']) && $paramsdata['type'] == 'FILE' && isset($_FILES[$name]);
/* (3) Si le paramètre est obligatoire et qu'il n'est pas donné -> erreur */
if( !isset($params[$name]) && !$optional && !$isFile )
return false;
/* (4) Si le type n'est pas defini, on a pas besoin de le vérifier */
if( !isset($paramsdata['type']) )
continue;
/* (5) Si le paramètre est optionnel et n'est pas donné */
if( $isFile || $optional && (!isset($params[$name]) || is_null($params[$name])) ){
// On le crée le param optionnel avec la valeur NULL
$params[$name] = null;
// On donne une référence vers le fichier, si c'en est un
if( $isFile )
$params[$name] = &$_FILES[$name];
continue; // On passe au paramètre suivant
/* (6) Si le paramètre est renseigné */
}else
// Si la verification est fausse, on retourne faux
if( !Database::check($paramsdata['type'], $params[$name]) )
return false;
}
/* [3] Gestion du retour, si tout s'est bien passe
=========================================================*/
return true;
}
/* AJOUT DES OPTIONS A PARTIR DE LA CONFIGURATION
*
*/
private function buildOptions(){
/* [0] On récupère les options de la méthode en cours
=========================================================*/
$method = $this->modules[$this->path['module']][$this->path['method']];
/* (1) Si 'option' n'est pas défini (ou incorrect), on met les valeurs par défaut */
if( !isset($method['options']) || !is_array($method['options']) )
return true;
/* (2) Par défaut on définit les options par défaut */
$this->options = self::$default_options;
/* (3) On récupère les options données */
$options = $method['options'];
/* [1] Gestion des différentes options
=========================================================*/
foreach($options as $option=>$value){
/* (1) On ne prend en compte l'option que si elle est dans les options par défaut */
if( !isset(self::$default_options[$option]) )
continue;
/* (2) Le type de la valeur doit être le même que celui de la valeur par défaut */
if( gettype($value) != gettype(self::$default_options[$option]) )
continue;
/* (3) Si tout est bon, on définit la valeur */
$this->options[$option] = $value;
}
return true;
}
/* RENVOI LE CHEMIN D'AMORCAGE DE LA METHODE
*
* @return path<Array> Retourne le chemin d'amorcage de la requete
@ -256,4 +546,4 @@
}
?>
?>

View File

@ -1,20 +1,20 @@
<?php
<?php
namespace manager;
// FORMAT:
//
// FORMAT:
//
// path: "nomModule/nomMethode"
// data1: {donnee1}
// data2: {donnee2}
// ...
//
//
//
//
//
//
//
//
class ModuleAnswer{
class ModuleResponse{
// Attributs prives utiles (initialisation)
private $data;
@ -136,8 +136,20 @@
*/
public function serialize(){
// Code Http
ManagerError::setHttpCode($this->error);
// Type de contenu
// header('Content-Type: application/json');
// On rajoute l'erreur au message
$returnData = array_merge( array('ModuleError' => $this->error), $this->data );
$returnData = array_merge(
array(
'ModuleError' => $this->error,
'ErrorDescription' => ManagerError::explicit($this->error)
),
$this->data
);
return json_encode($returnData);
@ -147,4 +159,4 @@
}
?>
?>

View File

@ -1,18 +1,18 @@
<?php
<?php
namespace manager;
// FORMAT:
//
// FORMAT:
//
// path: "nomModule/nomMethode"
// data1: {donnee1}
// data2: {donnee2}
// ...
//
//
//
//
//
//
//
//
class Repo{
@ -54,7 +54,7 @@
=========================================================*/
// Modules specifies
$this->repositories = json_decode( ResourceDispatcher::getResource(self::$config_path), true );
// Gestion de l'erreur de parsage
if( $this->repositories == null ){
$this->error = ManagerError::ParsingFailed;
@ -72,7 +72,7 @@
}
// Type de @data (optionnel)
$data = (is_array($data)) ? $data : array();
$data = (is_array($data)) ? $data : array();
/* [2] Verification du chemin (existence repo+methode)
@ -165,7 +165,7 @@
$this->error = ManagerError::UnknownMethod;
return false; // On retourne FALSE, si erreur
}
/* [4] Enregistrement du chemin et renvoi de SUCCESS
@ -194,4 +194,4 @@
}
?>
?>

View File

@ -1,11 +1,33 @@
<?php
// On definit la racine __ROOT__ si c'est pas deja fait
/* [0] On definit la racine __ROOT__ si c'est pas deja fait
=========================================================*/
if( !defined('__ROOT__') ) define('__ROOT__', dirname(dirname(__FILE__)) );
/* [1] On définit __SERVER_HOST__ et __SERVER_ROOT__ si c'est pas déja fait
=========================================================*/
if( !defined('__SERVER_HOST__') || !defined('__SERVER_ROOT') ){
/* (1) On charge le fichier de configuration */
$json = json_decode( file_get_contents(__ROOT__.'/config/server.json'), true );
// Si pas d'erreur, on définit
if( !is_null($json) ){
/* (2) Gestion de la config si server local ou remote */
if( !isset($_SERVER['SERVER_NAME']) || !checkdnsrr($_SERVER['SERVER_NAME'], 'NS') )
$config = $json['local'];
else
$config = $json['remote'];
/* (3) Création des constantes */
define('__SERVER_HOST__', $config['host']);
define('__SERVER_ROOT__', $config['root']);
}
}
@ -22,28 +44,6 @@
/* APPEL DYNAMIQUE DES CLASSES PASSEES EN PARAMETRE
*
* @classes<Array> Tableau contenant le nom des classes
*
*/
function autoload($classes){
foreach($classes as $class){
$name_only = substr(strrchr($class, '\\'), 1);
var_dump('use '.$class.' as '.$name_only.';');
eval('use '.$class.' as '.$name_only.';');
}
}
/* AUTOLOADER
*
* @className<String> Nom de la classe appelee
@ -63,7 +63,7 @@
require_once $path; // on inclue le fichier
}
// On definit l'autoloader comme autoloader (obvious)
spl_autoload_register('autoloader', false, true);
@ -73,4 +73,15 @@
/* On demarre la session securisee PHP
=========================================================*/
\manager\sessionManager::session_start();
?>
/* [3] Gestion des droits des utilisateurs
=========================================================*/
/* (1) Retourne si l'utilisateur est connecte ou non */
function connected(){ return isset($_SESSION['permission']) && count($_SESSION['permission']) > 0; }
/* (2) Retourne si l'utilisateur a le status en question */
function permission($type){ return connected() && in_array($type, $_SESSION['permission']); }
?>

116
manager/repo/parentRepo.php Normal file
View File

@ -0,0 +1,116 @@
<?php
namespace manager\repo;
use \manager\Database;
class parentRepo{
// mise à jour du nom de la table (pour les enfants)
protected static function table_name(){ static $table_name = null; return $table_name; }
/* GESTION DES GETTERS dynamiques
*
* @method<String> Nom du getter du type 'getAll' ou 'getX' avec 'X' une colonne de la table en question
* @args<Array> Liste des arguments, $args[0] est la valeur du getter (sauf pour 'getAll')
*
* @return lines<Array> Retourne le résultat du fetchAll()
*
*/
public static function __callStatic($method, $args){
// Si static::table_name() NULL
if( is_null(static::table_name()) ) return false;
/* [1] On vérifie que la méthode est 'getX', avec X une chaine
=========================================================*/
// Si c'est pas le bon format, on retourne une erreur
if( !preg_match('/^get(?:By(\w+)|(All))$/', $method, $matches) ) return false;
/* [2] On charge la liste des colonnes de la table
=========================================================*/
$getColumns = Database::getPDO()->query('SHOW COLUMNS FROM '.static::table_name());
$cols = Database::delNumeric( $getColumns->fetchAll() );
$table_columns = array(
'_PRIMARY_' => array() // Contiendra les champs de la clé primaire
);
// On ajoute la liste des colonnes
foreach($cols as $column){
// On enregistre la clé primaire (si elle l'est)
if( $column['Key'] == 'PRI' ) array_push($table_columns['_PRIMARY_'], $column['Field']);
array_push($table_columns, $column['Field']);
}
/* [3] On vérifie que la valeur après 'get' est dans $table_columns
=========================================================*/
$columnName = strtolower($matches[1]);
$getAll = count($matches) > 2; // Si 'getAll'
$getById = $columnName == 'id';
$getSomething = count($args) > 0 && in_array($columnName, $table_columns); // Si 'getX', et 'X' dans la liste des colonnes
// Si ni 'getAll' ni 'getSomething' -> erreur
if( !$getById && !$getAll && !$getSomething ) return false;
/* [4] On rédige la requête
=========================================================*/
$getRequestString = 'SELECT * FROM '.static::table_name();
// Si c'est 'getById', on ajoute une condition (clé primaire)
if( $getById ){
// S'il manque un paramètre, on retourne une erreur
if( count($args) < count($table_columns['_PRIMARY_']) )
return false;
// Pour chaque clé primaire (si elle est composée)
foreach($table_columns['_PRIMARY_'] as $i=>$primary_column)
// Première ligne
if( $i == 0 ) $getRequestString .= ' WHERE '.$table_columns['_PRIMARY_'][$i].' = :primary'.$i;
// Lignes suivantes
else $getRequestString .= ' AND '.$table_columns['_PRIMARY_'][$i].' = :primary'.$i;
// Si c'est 'getSomething', on ajoute une condition
}else if( $getSomething )
$getRequestString .= ' WHERE '.$columnName.' = :value';
$getRequestString .= ' ORDER BY 1 ASC';
// On prépare la requête
$getRequest = Database::getPDO()->prepare($getRequestString);
/* [5] On exécute la requête
=========================================================*/
// Si 'getById', on compose la clé primaire
if( $getById ){
$pdo_vars = array();
foreach($table_columns['_PRIMARY_'] as $i=>$primary_column)
$pdo_vars[':primary'.$i] = $args[$i];
$getRequest->execute( $pdo_vars );
// Si 'getSomething', on ajoute le champ
}else
$getRequest->execute(array(
':value' => ($getSomething||$getById) ? $args[0] : null
));
/* [6] On récupère le résultat
=========================================================*/
return Database::delNumeric( $getRequest->fetchAll() );
}
}
?>

View File

@ -26,7 +26,7 @@
// On definit l'id session si donne en argument
if( $session_id != null )
session_id( $session_id );
// Precaution: on met a jour le cookie
setcookie('PHPSESSID', session_id(), time()+60*30 );
@ -45,7 +45,7 @@
/*******************/
private static function update_token(){
$token = self::$prefix.self::secure_sha1(uniqid());
// On definit le token en session
$_SESSION['session_token'] = $token;
@ -53,11 +53,17 @@
$_COOKIE['session_token'] = $_SESSION['session_token'];
setcookie('session_token', $_COOKIE['session_token'], time()+60*30 );
}
/************/
/* AMORCEUR */
/************/
public static function session_start(){
// \session_start();
// return;
/* [1] Génération et Gestion des donnees a utiliser
==============================================================*/
// On genere le hash a partir des donnees personnelles
@ -79,16 +85,16 @@
\session_start();
// On verifie l'id session (5 premiers chars du hash des donnees perso)
$valid_sessid = strpos( session_id(), substr(self::$prefix,0,5) ) === 0;
$valid_sessid = strpos( session_id(), substr(self::$prefix,0,5) ) === 0;
// Si id session incorrect ou pas de token
// Si id session incorrect ou pas de token
if( !$valid_sessid )
self::reset_session( $sessid ); // On initialise la session (bon id session)
// si id session invalide
/* [3] Verification du token
==============================================================*/
// On verifie que le token est valide

12
test/ajaxTest.php Normal file
View File

@ -0,0 +1,12 @@
<?php define('__ROOT__', dirname(dirname(__FILE__)) );
require_once __ROOT__.'/manager/autoloader.php';
debug();
// DEFAULT SESSION CREDENTIALS
echo '{"data1": "'.session_id().'",';
echo '"data2": "'. ( $_SESSION['session_token']==$_COOKIE['session_token'] ? 'YES' : 'NO' ). '"}';
?>

View File

@ -1,4 +1,4 @@
<?php define('__ROOT__', dirname(__FILE__) );
<?php define('__ROOT__', dirname(dirname(__FILE__)) );
require_once __ROOT__.'/manager/autoloader.php';
use \manager\ModuleRequest;
@ -167,7 +167,7 @@
/* TEST DU DISPATCHER DES MANAGERS
*
* @return nomRetour<typeRetour> Description du retour
*/
function testModuleDispatcher(){
@ -189,7 +189,7 @@
/* TEST DU DISPATCHER DES REPO
*
* @return nomRetour<typeRetour> Description du retour
*/
function testRepoDispatcher(){
@ -305,4 +305,4 @@
// new ResourceDispatcher('f/svg/search/st/sub-menu-side/ff0000', true);
?>
?>

53
test/sessionTest.php Normal file
View File

@ -0,0 +1,53 @@
<?php define('__ROOT__', dirname(dirname(__FILE__)) );
require_once __ROOT__.'/manager/autoloader.php';
debug();
// DEFAULT SESSION CREDENTIALS
var_dump( 'SESSID---'.session_id() );
var_dump( 'TOKEN----'.( $_SESSION['session_token']==$_COOKIE['session_token'] ? 'YES' : 'NO' ) );
?>
<div id=ajax1>NO VALUE</div><br>
<div id=ajax2>NO VALUE</div>
<br><br><br><br>
<button id=testAjax>LOAD AJAX</button>
<script type='text/javascript'>
function pHandler(data){
var parsedData = JSON.parse(data);
document.getElementById('ajax1').innerHTML = parsedData.data1;
document.getElementById('ajax2').innerHTML = parsedData.data2;
}
function testAjax(){
var xhr; // object ajax
if(window.XMLHttpRequest) // IE7+, Firefox, Chrome, Opera, Safari
xhr = new XMLHttpRequest();
else // IE5, IE6
xhr = new ActiveXObject('Microsoft.XMLHttpRequest');
xhr.onreadystatechange = function(){
if( xhr.readyState == 4 ) // si la requête est terminée
if( [0,200].indexOf(xhr.status) > -1 ) // si fichier existe et reçu
pHandler(xhr.responseText);
else // si code d'erreur retourne null
pHandler();
}
xhr.open( 'POST', '/test/ajaxTest.php', true );
xhr.send();
}
document.getElementById('testAjax').onclick = testAjax;
</script>

10
todo.md
View File

@ -135,7 +135,7 @@
- [x] [view/][view] Ajout de "use CLASS;"
- [x] Ajout de "use CLASS;" dans les fichiers pour simplifier la lisibilite
- [x] [phpunit/sessionManager] test unitaires du manager de session php
- [x] [ModuleAnswer] Gestion des erreurs au niveau interne des Modules
- [x] [ModuleResponse] Gestion des erreurs au niveau interne des Modules
- [x] [autoloader][phpunit/bootstrap.php] Correction des bugs de $_SERVER avec PHPUnit -> autoloader + bootstrap personnalise
- [x] [sessionManager] Import de sessionManager
- [x] [phpunit/tests/Database_*] Tests unitaire de delNumeric()
@ -156,8 +156,8 @@
- [x] [view/*.php] Modification des views
- [x] Mise a jour / Modification / Correction des images du menu-side
- [x] [ModuleRequest->dispatch] Passage de l'erreur a la reponse
- [x] [ModuleAnswer->serialize] Integration de l'erreur dans la serialisation
- [x] [ModuleAnswer->get+getAll] Accesseurs aux donnees de la reponse
- [x] [ModuleResponse->serialize] Integration de l'erreur dans la serialisation
- [x] [ModuleResponse->get+getAll] Accesseurs aux donnees de la reponse
- [x] Gestion des erreurs
- [x] [ModuleError::explicit] Explicitation
- [x] Conception du systeme de delegation des managers
@ -165,8 +165,8 @@
- [x] [ModuleRequest->__construct] Inline (en php)
- [x] [ModuleRequest::fromString] Serialise (en json <String>)
- [x] [ModuleRequest::FromURL] Par url (POST)
- [x] [ModuleAnswer] Module Answer
- [x] [ModuleAnswer->serialize] Serialisation pour renvoi
- [x] [ModuleResponse] Module Answer
- [x] [ModuleResponse->serialize] Serialisation pour renvoi
- [x] [index.php] Gestion de url/api avec donnees POST
- [x] [ModuleRequest->getFunctionCaller] Correction semantique du chemin d'amorcage, utilisation de tableau
- [x] Gestion des erreurs

View File

@ -54,7 +54,7 @@
echo "<section data-sublink='view' class='list'>";
// echo 'Liste des utilisateurs: <br>';
// si erreur, on affiche l'explicitation
if( $answer->error != ManagerError::Success ){
// var_dump( ManagerError::explicit($answer->error) );
@ -72,14 +72,14 @@
foreach( $answer->get('machines') as $machine){
$clusters = new Repo('machine/getClusters', array($machine['id_machine']));
$clusters = $clusters->answer();
echo "<article class='inline-box' id='".$machine['id_machine']."'>";
// Nom de la machine
echo "<span class='title'>#".$machine['name']."</span>";
// Icone vers la suppression
// Icone vers la suppression
echo "<span class='link_remove' data-machine='".$machine['id_machine']."'>";
echo ResourceDispatcher::getResource('f/svg/remove/st/sub-menu-side');
echo "</span>";
@ -91,12 +91,12 @@
// Code RFID
echo "<span class='code'>";
echo ResourceDispatcher::getResource('f/svg/card/st/container');
echo "<span>";
echo $machine['code'];
echo "</span>";
@ -105,7 +105,7 @@
// Groupes de la machine
echo "<span class='groups'>";
echo ResourceDispatcher::getResource('f/svg/group/st/container');
if( $clusters != false )
foreach($clusters as $cluster)
echo "<span>".$cluster['name']."</span>";
@ -163,7 +163,7 @@
// Indice du resultat
echo "<span class='remove_search_view'>machine <span class='remove_search_num'>0</span> sur <span class='remove_search_sum'>0</span></span><br><br>";
echo "<button id='remove_search_submit' class='search'>Trouver</button><br>";
echo "<br><br><hr class='OR' data-label='PUIS' /><br><br>";
echo "<input id='remove_code' type='text' placeholder='CO-DE-RF-ID'><br>";
@ -194,9 +194,9 @@
// Indice du resultat
echo "<span class='edit_search_view'>machine <span class='edit_search_num'>0</span> sur <span class='edit_search_sum'>0</span></span><br><br>";
echo "<button id='edit_search_submit' class='search'>Trouver</button><br>";
echo "<br><br><hr class='OR' data-label='PUIS' /><br><br>";
echo "<input id='edit_code' type='text' placeholder='Code'><br>";
echo "<input id='edit_name' type='text' placeholder='Name'><br>";
echo "<button id='edit_submit' disabled>Modifier</button>";