<?php
#=
/****************************************************************************
Copyright 2002, Daniel Deycard, Jacques Messines, Alain Pomirol
(CATICE Académie de Bordeaux)

This file is part of Gibii.

Gibii is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

Gibii is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Gibii; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

****************************************************************************
*/

require_once("../include/define.inc.php");
require_once("../incorporation/util.class.php");
require_once("../include/get.inc.php");
/****************************************************************************/
class qqun {
var $table;//celle à laquelle doit appartenir l'individu
var $champ_id;//libellé que porte la clé primaire dans la table sous jacente (eleve_id, prof_id)
var $id;//l'autonumber de la table dont est issu l'objet
var $nom, $prenom, $dnaiss, $mdp;//mdp est codé par md5
var $tpren;//tableau des prenoms découpés, sans accents ni majuscules
var $prox;//indice de proximité des prénoms avec un autre user (réel de [0,1])
var $civil, $ident_nat, $uid, $tag;
var $compte_id;
var $login, $statut;
var $etab_id;
var $annee;//la dernière année scolaire connue (pour repérer les disparus notamment)
var $findcount;//nb lignes trouvées dans un findfirst
var $ressource;//privée : pour findfirst, findnext
var $messages;//informations et erreurs rencontrées pour le nouvel arrivant
var $map;//tableau associant un identificateur à un n° dans le fichier texte
var $verbose; //affichage ou non des individus traités
var $entete; //tableau des champs rencontrés dans la ligne d'en-tête du fichier	
var $sep;	//séparateur de champs dans le fichier texte
var $codemap; //0 si mappage correct

function qqun($st) {
//$t est la table à laquelle appartient la personne
$this->statut = ucwords($st);//Eleve ou Prof
$this->table = $st . 's';//eleves ou profs
$this->champ_id = $st . '_id';//eleve_id ou prof_id
$this->login = '';//pourra être affecté si besoin...
$this->civil = '';
$this->uid = '';
$this->tpren = array();
$this->messages = array();
$this->map = array();
$this->verbose=true; //mis ensuite à false seulement dans le batch d'incorporation
$this->entete=array();
$this->sep='';
}

function setcommon($obj) {
//partie commune aux descendants
//$obj vient de eleves, profs et le champ récupéré de la table
//s'appelle 'eleve_id' ou 'prof_id', d'où cette écriture avec accolades
$this->id = $obj->{$this->champ_id};
$this->ident_nat = $obj->ident_nat;
$this->nom = stripslashes($obj->nom);
$this->prenom = stripslashes($obj->prenom);
$this->decoupeprenom();
$this->dnaiss = $obj->dnaiss;
if (isset($obj->mdp))
    $this->mdp = $obj->mdp;
//sinon on s'en fout : l'indiv ne vient pas d'une table new
$this->civil = $obj->civil;
$this->compte_id = $obj->compte_id;
$this->etab_id = $obj->etab_id;
$this->ident_nat = $obj->ident_nat;
$this->uid = $obj->uid;
$this->tag = $obj->tag;
$this->annee = $obj->annee;
}

function setbyid($id) {
//récupère l'objet de SA table, avec toutes ses données utiles
$res = query("select * from $this->table where $this->champ_id=$id;");
$obj = mysql_fetch_object($res);
if ($obj)
    $this->setbyobj($obj);//dans les descendants eleve et prof
mysql_free_result($res);
return $obj;
}


function seterror($cause) 	{
$this->addmess($cause);
return false;//fait retourner false à la fonction appelante (signal d'erreur)
}

function addmess($contenu, $codecoul = '') {
if ($this->verbose) {
    if ($codecoul) $contenu = "<font color=\"$codecoul\">" . $contenu . '</font>';
    $this->messages[] = $contenu;
	}
}
	

function vider() {
//il s'agit plutôt d'une revirginisation de l'objet
//pour éviter des mélanges lors de champs absents du suivant
//ne désaffecte que les champs qui sont relatifs à un individu
//donc tous sauf ceux que l'on garde, à savoir :
$garde = array('table', 'champ_id', 'etab_id', 'annee', 'findcount', 'ressource',
    'messages', 'login', 'civil', 'statut', 'map', 'verbose', 'entete','sep');
foreach (get_object_vars($this) as $champ => $valeur) {
    if (!in_array($champ, $garde, false)) unset($this->$champ);
	}
$this->login = '';//pourra être affecté si besoin...
$this->civil = '';
$this->messages = array();
$this->tpren = array();
}

function delete() {
//supprime l'individu associé à l'objet de sa table de départ
return query("DELETE FROM $this->table WHERE $this->champ_id=$this->id;");
}


//découpage des prénoms
function decoupeprenom() {
//buffers pour éviter les recalculs
static $ACCENTS_MINUS_AVEC = "àâäéèêëîïôöùûüçñ", $ACCENTS_MINUS_SANS =
    "aaaeeeeiioouuucn", $taccents, $tsans;
if (!isset($taccents)) {
    $taccents = array();
    for ($i = 0; $i < strlen($ACCENTS_MINUS_AVEC); $taccents[] = $ACCENTS_MINUS_AVEC[$i++]);
    $tsans = array();
    for ($i = 0; $i < strlen($ACCENTS_MINUS_SANS); $tsans[] = $ACCENTS_MINUS_SANS[$i++]);
	}
//Les accents sont supprimés, le . e
//str_replace($taccents,$tsans,$this->prenom);
$this->tpren = preg_split("/[\s,\.-]+/", strtolower(str_replace($taccents, $tsans,
    	$this->prenom)), -1, PREG_SPLIT_NO_EMPTY);
}


function proximitePrenoms($autre) {
//compare les tableaux de prénoms normalisés de $this et de autre
//retourne une évaluation dans [0,1]
$ct = count($this->tpren);
$ca = count($autre->tpren);

if ($ct == 1 && $ca >= 1 && $this->tpren[0] == $autre->tpren[0])
    $prox = 1;
elseif ($ct == 0 || $ca == 0)
    $prox = 1;
else {
    $cinter = count(array_intersect($this->tpren, $autre->tpren));
    $prox = $cinter / ($ct + $ca - $cinter);//card(intersection)/card(réunion)
	}
return $prox;
}

function qhomo($retunique=false) {
// s'il existe un homo SATISFAISANT avec CERTITUDE, la fonction retourne cet homo ($vieux)
// Dans tous les autres cas : 
//		- si $retunique=false, retourne le nombre d'homos limité à 2 : 0, 1 ou 2
//    		(on se moque de la quantité réelle car 2, c'est déjà trop pour conclure)
// 		- si $retunique=true, retourne $vieux s'il est unique, même si ce n'est pas un homo parfait

$classereelle = get_class($this);
$vieux = new $classereelle();
$nomquote = addslashes($this->nom);
//il n'y a plus d'apostrophe dans $this->tpren
$nbh=0;
$existe = $vieux->findfirst("nom='$nomquote' AND tag=0");
while ($existe && $nbh<2) { //pas la peine de chercher au-delà de 2
    if (!empty($this->uid) && $this->uid == $vieux->uid) {
        //trouvé sans ambiguïté par identificateur national
        $vieux->findclose();
        return $vieux; //certitude
    	}
    $vieux->prox = $this->proximitePrenoms($vieux);//fonction symétrique en $this et $vieux

    if (!empty($vieux->ident_nat)) {
    	$punder=strpos($vieux->ident_nat,'_');
		if ($punder===false) {
			$vieux->ident_nat=sprintf("%05s",$vieux->ident_nat).'_' . $vieux->etab_id;
			}
		else {
			//$punder=4 par exemple. La longueur de la partie avant _ est donc 4. Il faut la normaliser à 5
    		if ($punder<5) $vieux->ident_nat=str_repeat('0',5-$punder).$vieux->ident_nat;
    		else if ($punder>5) $vieux->ident_nat='';
        	}
		}
    $midgep = !empty($this->ident_nat) && $this->ident_nat == $vieux->ident_nat;

    if ($vieux->dnaiss == $this->dnaiss || $midgep)   {
        if ($vieux->prox > PROX2 || $midgep) {
            if ($this->etab_id == $vieux->etab_id || $this->annee != $vieux->annee) {
                //repasse ou bascule d'un ancien de l'étab ou ancien d'un étab voisin
                //sous-entendu : on ne pique pas en mode automatique un élève de la même année à un étab voisin
                //Dans ce cas seulement on décide que "c'est le bon" et on oublie les autres éventuels
                $vieux->findclose();
                return $vieux;//pas la peine de continuer
            	}
            else {
                $nbh++;
            	}
        	}
        elseif ($vieux->prox > PROX1) {
            //dans tous les autres cas, on se contente au mieux de noter qu'il y a un homonyme
            $nbh++;//sinon, prénoms trop différents
        	}
        else ;//on ne conserve rien zt on passe au tour de boucle suivant 
    	}
    else {
        //dates de naissances différentes : on est plus exigeant sur la ressemblance des prénoms
        if ($vieux->prox > PROX3 || $midgep) $nbh++;
        else ;//on ne conserve pas
    	}
    $existe = $vieux->findnext();
	}
$vieux->findclose();
//print "<br>Homonymes : ";print_r($tbl);print "<br>";
if ($retunique && $nbh==1) return $vieux; //pour le cas où "associer si unique" dans incertains3
unset($vieux);
return $nbh;//retourne le nombre d'homonymes dans le cas où il y a ambiguïté
}
	

function findhomo($vieux=false) {
if (!$vieux) {
	$classereelle = get_class($this);
    $vieux = new $classereelle();
    $nomquote = addslashes($this->nom);
    //il n'y a plus d'apostrophe dans $this->tpren
    $existe = $vieux->findfirst("nom='$nomquote' AND tag=0");
	}
else  $existe = $vieux->findnext();

//dans un point d'exécution ultérieur, attention à ne pas stocker les $vieux successifs
//retournés par cette fonction : en PHP5, ils ont tous la même adresse puisqu'il n'y a qu'un seul objet $vieux

while ($existe) {
    if (!empty($this->uid) && $this->uid == $vieux->uid) {
        //trouvé sans ambiguïté par identificateur national
        //pas de findclose car ce n'est peut-être pas le dernier
        return $vieux;//pas la peine de continuer à engranger dans $tbl
    	}
    $vieux->prox = $this->proximitePrenoms($vieux);//fonction symétrique en $this et $vieux

    if (!empty($vieux->ident_nat)) {
    	$punder=strpos($vieux->ident_nat,'_');
		if ($punder===false) {
			$vieux->ident_nat=sprintf("%05s",$vieux->ident_nat).'_' . $vieux->etab_id;
			}
		else {
			//$punder=4 par exemple. La longueur de la partie avant _ est donc 4. Il faut la normaliser à 5
    		if ($punder<5) $vieux->ident_nat=str_repeat('0',5-$punder).$vieux->ident_nat;
    		else if ($punder>5) $vieux->ident_nat='';
        	}
		}
    $midgep = !empty($this->ident_nat) && $this->ident_nat == $vieux->ident_nat;

    if ($vieux->dnaiss == $this->dnaiss || $midgep)   {
        if ($vieux->prox > PROX2 || $midgep) {
            return $vieux;//pas la peine de continuer
        	}
        elseif ($vieux->prox > PROX1) {
            return $vieux;//pas la peine de continuer
    		}
    	else ;//
    	}
    elseif ($vieux->prox > PROX3 || $midgep) {
    	//dates de naissances différentes : on est plus exigeant sur la ressemblance des prénoms
		return $vieux;
		}
    else ; // pas bon le vieux
    	
    $existe = $vieux->findnext();
	}
$vieux->findclose(); //là, c'est sûr, il n'y a plus de vieux
unset($vieux);
return false;//
}

function firsthomo() {
return $this->findhomo();		
}	
function nexthomo($vieux) {
return $this->findhomo($vieux);		
}	
function closehomo() {
$this->findclose();
}			

function findfirst($condition = "1")    {
@mysql_free_result($this->ressource);
$this->ressource = query("select * from $this->table where $condition;");
$obj = false;
$this->findcount = 0;
if ($this->ressource) {
    $this->findcount = mysql_num_rows($this->ressource);
    if ($this->findcount) {
        $obj = mysql_fetch_object($this->ressource);
        $this->setbyobj($obj);
    	}
	}
return $obj;
}

function findnext() {
$obj = false;
if (isset($this->ressource)) {
    $obj = mysql_fetch_object($this->ressource); //false si c'est fini
    if ($obj) $this->setbyobj($obj);
    else {
        mysql_free_result($this->ressource);
        unset($this->ressource);
        unset($this->findcount);
    	}
	}
return $obj;
}

function findclose() {
unset($this->ressource);
unset($this->findcount);
}

function creerloginbase($masque) {
//méthode privée, en principe
//interpréter les codes ainsi :
//   n=nom, p=prénom, d=division ou discipline, #=numéro, b=naissance (born)
$result = '';
$masque = normernom($masque);
$nompropre = normernom($this->nom);
$prenompropre = normernom($this->prenom);
$inom = 0;
$igroup = 0;
$ipre = 0;
$idiv = 0;
$idnaiss = 0;
for ($i = 0; $i < strlen($masque); $i++) {
    switch ($masque[$i]) {
        case "n":
            $result .= carvalide($nompropre, $inom, "");
            break;
        case "p":
            $result .= carvalide($prenompropre, $ipre, "");
            break;
        case "d":
            $result .= carvalide($this->getgroupvalue(), $igroup, _ALPHANUM);
            break;
        case "b":
            $result .= carvalide($this->dnaiss, $idnaiss, _NUM);
            break;
        case "#":
            $result .= carvalide($this->ident_nat, $inum, _NUM);
            break;
        case "N":
            $result .= normernom($nompropre);
            break;
        case "P":
            $result .= normernom($prenompropre);
            break;
        default:
            $result .= $masque[$i];
            break;
    	}
	}
return $result;
}

function creerloginunique($masque) {
//crée le login d'un nouveau en tenant compte d'un éventuel début de login proposé
//(voir plus bas)
if (!empty($this->login))
{
    //cas d'une nouvelle année avec $this affecté dans la ligne du fichier texte
    $login = normernom($this->login);
}
else
    $login = $this->creerloginbase($masque);

//recherche du premier login possible à partir de la "base" contenue dans $login
//(par exemple, $login contient initialement bm ou ben.mar)
$result = query("SELECT login FROM comptes
        WHERE login LIKE '" . addslashes($login) . "%'
          AND etab_id=$this->etab_id");
if (mysql_num_rows($result))
{//il y a déjà des login analogues
    $long = strlen($login);
    while (list($loginexistant) = mysql_fetch_array($result))
    {
        $finlogin = substr($loginexistant, $long);
        $tlog[] = $finlogin;
    }
    $suffixe = '';
    while (in_array($suffixe, $tlog))
        $suffixe++;
    $login .= $suffixe;
}
mysql_free_result($result);
$this->login = $login;
return $login;
}


function ajoutertable($tag) {
$nomquote = addslashes($this->nom);
$prenomquote = addslashes($this->prenom);
$nomchampgroupe = $this->getgroupfield();
$valeurgroupe = $this->getgroupid();
query("INSERT INTO $this->table SET
           nom='".secs($this->nom)."',
           prenom='".secs($this->prenom)."',
           dnaiss='".secs($this->dnaiss)."',
           ident_nat='".secs($this->ident_nat)."',
           uid='".secs($this->uid)."',
           $nomchampgroupe=".seci($valeurgroupe)." ,
           civil='".secs($this->civil)."',
           annee=".seci($this->annee).",
           tag=".seci($tag)." ,
           etab_id=$this->etab_id;");
$this->id = mysql_insert_id();
return $this->id;
}


function updatetable(&$old) {
//modifie la table sous-jacente
//nom, prenom, dnaiss sont inchangés
//old est un ancien (eleve ou prof)
$nomchampgroupe = $this->getgroupfield();
$valeurgroupe = $this->getgroupid();
$this->id = $old->id;//on identifie le nouveau avec l'ancien (pour l'instant, l'id dans la table)
//On essaie d'affecter le prénom s'il était vide

if (empty($old->prenom) && !empty($this->prenom)) {
    $setprenom = " prenom='" . secs($this->prenom) . "', ";
    $modifpren=true;
    }
else {
    $setprenom = "";
    $modifpren=false;
    }
$ret = query("UPDATE $this->table
       SET $setprenom
	   	   civil='".secs($this->civil)."',
           dnaiss='$this->dnaiss',
           ident_nat='".secs($this->ident_nat)."',
           uid='".secs($this->uid)."',
           $nomchampgroupe='".secs($valeurgroupe)."',
           etab_id=$this->etab_id,
           annee=$this->annee,
           tag=0
     WHERE $this->champ_id=$old->id");
//voir ci-dessus dans ajoutertable
return $modifpren;
}


function get_create_classe($divcod) {
    //utilisé pour élèves (paramètre=classe) et profs (paramètre=classe ou £groupe)
    static $tabclasses = array(), $tabdivgro = array();
    //si $divcod commence par £, c'est un groupe
if ($divcod) {
    if ($divcod[0] == '£') {
        $divcod = substr($divcod, 1);
        $divgro = 'G';
        $m1 = 'groupe créé';
        }
    else {
        $divgro = 'D';
        $m1 = 'classe créée';
        }
    }
else {
    //classe au nom vide. On crée un nom bidon
    $divcod = "SansNom_".$this->etab_id;
    $divgro = 'D';
    $m1 = 'classe créée avec un nom par défaut';
    }

if (count($tabclasses) == 0) {
    //récupère un tableau : $tabclasses["3G3"]=7 par exemple
    $res = query("SELECT * FROM classes WHERE etab_id=$this->etab_id;");
    while ($row = mysql_fetch_object($res)) {
        $tabclasses[$row->divcod] = $row->classe_id;
        $tabdivgro[$row->divcod] = $row->divgro;
        }
    }

if (isset($tabclasses[$divcod])) {
    $id = $tabclasses[$divcod];
    }
else {
    $this->addmess(" [$divcod] : $m1. ");
    query("INSERT INTO classes SET divcod='".secs($divcod)."', divgro='".secs($divgro)."', etab_id=$this->etab_id;");
    $id = mysql_insert_id();
    $tabclasses[$divcod] = $id;
    $tabdivgro[$divcod] = $divgro;
    }
return $id;
}

		
function maptxt($nomfichier) {
//appelée par les descendants qui se contentent d'affecter les tableaux $this->sconet2gibii et $this->int2gibii
//spécifiques puis d'appeler cet ancêtre
//Le tableau $this->map sera affecté du rang de chacun des 'champs' du fichier texte
//retourne true si la ligne en param est un individu (et donc qu'un modèle n'a pas été trouvé)
/*
Stocker les couples (champ,rang) dans le tableau associatif $this->map. 
    Par exemple $this->map['nom']=5 signifie que le nom est en position 5 dans les données du fichier
	Les positions démarrent à 0
    L'astuce du 999 permet d'affecter '' lorsque le champ est absent.
*/
//recherche du séparateur et de la ligne d'en-tête
//------------------------------------------------
$fp=fopen($nomfichier,'r');
$sep="";$ligne1='';
$this->msgerreur='';
while (!feof($fp)) {
	$buffer = fgets($fp, 1024);
    if (preg_match ("/[,;\t]/",$buffer,$trouve)) {
	    $sep=$trouve[0];
	    $ligne1=$buffer;
	    break;
	    }
 	}

if (!$sep) {
	$this->codemap=-1;
	$this->msgerreur="attendu : virgule, point-virgule, tabulation";
	fclose($fp);
	return $this->codemap;
	}
$this->sep=$sep;

//normaliser l'en-tête
//--------------------
//supprimer les espaces de fin comme dans 'Nom ', s'arrêter au 1er blanc, transformer en minuscules
//et supprimer le 1 final éventuel.
$ligne1=desaccentuer(strtolower(trim($ligne1)));
while (strlen($ligne1) && $ligne1[strlen($ligne1)-1]==$sep) $ligne1=substr($ligne1,0,-1);
$this->entete=explode($sep,$ligne1);
$chiffres='0123456789';
foreach($this->entete as $idx=>$s) {
	$s=trim($s);
	$tmcle=explode(" ",$s);
	$s=$tmcle[0];
	//supprimer les chiffres à la fin
	while (!empty($s) &&  strpos($chiffres,$s[strlen($s)-1])!==false) $s=substr($s,0,-1);
	//si (empty($s)) //pas grave : on saute
	$this->entete[$idx]=$s;
	}

if (in_array('nom',$this->entete)) {
    //va bene per sconet
	$this->codemap=1;  //a priori : c'est Sconet sauf erreur
	//initialiser map (999 veut dire : le champ gibii correspondant n'y est pas
	foreach ($this->int2gibii as $gib) 	$this->map[$gib]=999;
	//affecter map des rangs des champs corrects trouvés dans l'en-tête
	foreach ($this->entete as $idx=>$sc) {
		if (isset($this->sconet2gibii[$sc])) {
			//en-tête reconnue
			$gib=$this->sconet2gibii[$sc];
			if ($this->map[$gib]==999) {
				$this->map[$gib]=$idx;
				}
			//sinon, on ignore le champ redondant... mais les champs classe, langues seront quand même comptés 
			}
		else {
			$this->codemap=-4;
			$this->msgerreur="$sc en position ".($idx+1); 
			fclose($fp);
			return $this->codemap;
			}
		}
	}
else {
   //mappage (par défaut d'en-tête) des versions <5.1
    $this->map = array_flip($this->int2gibii);
    $this->codemap=0; //pas d'en-tête
	rewind($fp); //le fichier débute par les données (donc on revient en arrière pour les exemples)
	}

$nbexemples=0;
while (!feof($fp) && $nbexemples<3) {
	$buffer = fgets($fp, 1024);
	if (!trim($buffer)) continue;
	if ($buffer[0]=="#") continue;
	$this->exemples[$nbexemples]=$buffer;
	$nbexemples++;
	}
return $this->codemap;
}

function getfield(&$ligne, $champ) {
if (isset($this->map[$champ]) && isset($ligne[$this->map[$champ]]))
    return trim($ligne[$this->map[$champ]]);
else
    return '';
}


function setbytxt(&$ligne) {
//affecte un objet à partir d'un tableau $ligne
//surchargée par les descendants

$ligne[999] = '';//sentinelle lorsqu'un champ n'est pas mappé dans le modèle
//valeur butoir (ou sentinelle)  $ligne[$this->map['xxx']] est ainsi toujours défini
//Tout est calculé par $ligne[$this->map['xxx']] ; si le champ 'xxx' n'est pas fourni dans le fichier texte,
//$this->map['xxx'] vaut 999 et par suite, $ligne[$this->map['xxx']] vaut ''.


//ident_nat
//---------
$ident_nat = $this->getfield($ligne, 'ident_nat');
if (!empty($ident_nat) && is_numeric($ident_nat)) {
    //de façon à conserver en tête le n° GEP
    //et préserver l'unicité entre tous les étabs
	$ident_nat=sprintf("%05s",$ident_nat).'_' . $this->etab_id;
    }
else $ident_nat=''; //on ne conserve pas si c'est douteux
$this->ident_nat = (string) $ident_nat;
$this->addmess("ident_nat vaut $ident_nat");

//nom
//---
$this->nom = $nom = ucwords(strtolower(ASCII2ANSI($this->getfield($ligne, 'nom'))));
if (empty($nom)) {
        $this->addmess("nom absent");
        return false;//signal d'erreur
    }

//prénom
//_-----
$this->prenom = $prenom = ucwords(strtolower(ASCII2ANSI($this->getfield($ligne,'prenom'))));
    $this->decoupeprenom();

//date de naissance
//-----------------
$this->dnaiss = normerdate($this->getfield($ligne, 'dnaiss'));


//pour le login et le mdp, on ne s'occupe que du strict minimum :
// les stocker s'ils existent. Les problèmes d'options seront traités par le
//programme appelant
//"""""""""""""""""""""
//login
//-----
$this->login = trim($this->getfield($ligne, 'login'));

//mot de passe
//------------
//s'il existe, le mdp peut être codé md5 ou pas
$this->mdp = $this->getfield($ligne, 'mdp');
return true;
}//function setbytxt

}//classe qqun


/*****************************************************************************/
/*****************************************************************************/

class eleve extends qqun {
var $classe_id, $divcod;
var $imp_diplome;
var $lv;//tableau des 3 langues vivantes pratiquées lv[1]=2 signifie LV=espagnol
//lv[3]=0 signifie : pas de LV3

function eleve() {
parent::qqun('eleve');
$this->lv = array();
}

function setbyobj($obj) {
//$obj est un résultat de mysql_fetch_array
$this->setcommon($obj);//affecte id, nom, prenom, dnaiss, GEP_id, tag
$this->classe_id = $obj->classe_id;
$this->imp_diplome = $obj->imp_diplome;
$this->divcod = $this->getgroupvalue();
//langues
$reslv = query("SELECT rang,disc_id FROM lien_eleves_disciplines WHERE eleve_id=$this->id");
while (list($r, $d) = mysql_fetch_array($reslv))
    $this->lv[$r] = $d;
mysql_free_result($reslv);
return $obj;
}

function getgroupname() {
return "Classe";
}//pour les affichages
function getgroupfield() {
return "classe_id";
}
function getgroupid() {
return $this->classe_id;
}

function getgroupvalue() {
static $classes = array();
if (!isset($classes[$this->classe_id])) {
    $res = query("SELECT classe_id,divcod FROM classes WHERE etab_id=$this->etab_id");
    while (list($id, $divcod) = mysql_fetch_array($res))
        $classes[$id] = $divcod;
    mysql_free_result($res);
}
if (isset($classes[$this->classe_id]))
    return $classes[$this->classe_id];
else
    return false;
}

function vider() {
parent::vider();
$this->lv = array();
}


//mettre la classe de l'élève dans les liens, comme si c'était un groupe d'élèves
function ajoutgroupe_en_lien() {
query("INSERT INTO lien_eleves_classes SET eleve_id=".seci($this->id).", classe_id=$this->classe_id");
}


//idem mais en update
function updategroupe_en_lien(&$old) {
//remplacer l'ancienne classe de l'élève ($old->classe_id) par la nouvelle ($this->classe_id)
//attention : les liens ont pu déjà être virés par une précédente bascule
//(à cause de nettoyer, à la fin de l'incorporation) donc le UPDATE ne ferait pas
//toujours le travail. D'où la complication ci-dessous.
//(NB : le mysql_affected_rows après un UPDATE pourrait aussi poser problème (-1)
query("UPDATE lien_eleves_classes SET classe_id=$this->classe_id
WHERE eleve_id=$old->id AND classe_id=$old->classe_id");
switch (mysql_affected_rows()) {
    case - 1://problème mysql : on ne fait rien (break ci-dessous)
    case 1:
        break;//cas normal
    case 0:
        query("INSERT INTO lien_eleves_classes
            SET eleve_id=$old->id, classe_id=$this->classe_id");
    default://plusieurs lignes ont été modifiées, donc on en profite pour corriger les doublons
        query("DELETE FROM lien_eleves_classes
            WHERE eleve_id=$old->id AND classe_id=$this->classe_id");
        //ils sont tous effacés, on en recrée un seul
        query("INSERT INTO lien_eleves_classes
            SET eleve_id=$old->id, classe_id=$this->classe_id");
}
}


function ajouteroptions() {
foreach ($this->lv as $i => $disc_id) {
    query("INSERT INTO lien_eleves_disciplines
          SET rang=$i, eleve_id=$this->id, disc_id=$disc_id");
}
}


function updateoptions() {
query("DELETE FROM lien_eleves_disciplines WHERE eleve_id=$this->id");
$this->ajouteroptions();
}


function insertcompte($champactif) {
$res = query("INSERT INTO comptes SET
             login='".secs($this->login)."', statut='Eleve',
             mdp='$this->mdp',
             actif=".seci($champactif)." ,
             nbsessions=0,
             eleve_id=$this->id,
             etab_id=$this->etab_id;");
if ($res) {
    $id = mysql_insert_id();
    query("UPDATE eleves SET compte_id=$id WHERE eleve_id=$this->id");
}
else
    $id = 0;
$this->compte_id = $id;
return $id;
}

function couperficelles() {
//travail effectué avant de supprimer $this de sa table
$id = $this->id;
//supprimer dans l'ordre reponses, demandes
$resdem = query("SELECT message_id FROM demandes WHERE eleve_id=$id");
while (list($message_id) = mysql_fetch_array($resdem)) {
    query("DELETE FROM reponses WHERE message_id=$message_id");
}
mysql_free_result($resdem);
query("DELETE FROM demandes WHERE eleve_id=$id;");
query("DELETE FROM lien_eleves_disciplines WHERE eleve_id=$id");
query("DELETE FROM lien_eleves_classes WHERE eleve_id=$id");
}


//méthode de classe
//tolère un codage personnalisé des langues dans le fichier texte
//d'importation et mémorise ce codage dans un tableau
function trouverlangue($code) {
static $precedents = array();
if (is_numeric($code))
    return $code;
$code = strtoupper(trim(substr($code, 0, 2)));
if (isset($precedents[$code]))
    return $precedents[$code];
$res = query("SELECT disc_id FROM disciplines WHERE code_langue LIKE '$code%'");
if (!list($disc_id) = mysql_fetch_array($res)) {
    $res = query("SELECT disc_id FROM disciplines WHERE libelle LIKE '$code%'");
    if (!list($disc_id) = mysql_fetch_array($res)) {
        mysql_free_result($res);
        return false;//plus d'espoirs
    }
    mysql_free_result($res);
}
$precedents[$code] = $disc_id;
return $disc_id;
}

function maptxt($nomfichier) {
/* Ligne type SCONET (seuls les noms des champs importent
INE,N° Interne,Nom,Prénom 1,Date de naissance,Sexe,Courriel élève,
Tel maison élève,Tel mobile élève,Division,Option 1,Option 2,Option 3,Option 4
*/

//Le champ INE n'apparaît pas ci-dessous puisque ces tables servent seulement à traduire les anciens champs GiBii

//ordre des champs par défaut d'en-tête (donc GiBii<5.1, donc sans INE)
$this->int2gibii = array(	'ident_nat', 'civil', 'nom', 'prenom', 
						 	'classe', 'dnaiss', 'login', 'mdp', 'lv', 
						 	999=>'uid');  // uid est "hors indice" pour l'ancien format gibii (champ uid non connu)

//mappage sconet vers gibii
$this->sconet2gibii = array(
			'ine' 		=> 'uid', 		
			'n°' 		=> 'ident_nat', 'n°interne' => 'ident_nat',	'ident_nat'	=> 'ident_nat',
			'sexe' 		=> 'civil', 	'civilite'	=> 'civil',		'civil'		=> 'civil',
			'nom' 		=> 'nom', 		'prenom' 	=> 'prenom', 	
			'division' 	=> 'classe', 	'classe'	=> 'classe',
			'date' 		=> 'dnaiss', 	'login' 	=> 'login',		'dnaiss'	=> 'dnaiss',
			'mdp' 		=> 'mdp', 	   	'option' 	=> 'lv',		'lv'		=> 'lv',
			
			);
			
$type_entete = parent::maptxt($nomfichier);

//tester l'existence du 2e champ obligatoire pour les élèves
if ($type_entete==2 && $this->map['classe']==999) $type_entete=-3;
return $type_entete;
}

//*********************************************************************************************
//Les champs d'un fichier texte eleves.txt créé manuellement pour GiBii ou créé par gep2txt.php
//[ident_nat], [civil], nom, prenom, [classe], [naissance], [login], [mdp crypté], [lv[, lv2[, lv3]]]

function setbytxt(&$ligne) {
//affecte un objet à partir d'un tableau $ligne. $ligne[999] revient avec ''.
if (!parent::setbytxt($ligne))
    return false;


//INE (11 car.)
//-------------
$this->uid = $this->getfield($ligne, 'uid');

//civilité  (le prénom est connu)
//--------
$civil = ucfirst(strtolower($this->getfield($ligne, 'civil')));
switch ($civil) {
    case 'M':
	case 'M.':
    case 'G':
    case 'H': 	$civil = 'M.';
        break;
    case 'Mme':
    case 'Me':
    case 'Mlle':
    case 'F':	$civil = 'Mlle';
        break;
    default: 	//on devine le sexe ! Si, si !
            if ($this->prenom) {
                $fin = substr($this->prenom, -1);
                if ($fin == 'e' || $fin == 'a') //critère simplet mais meilleur que 50%
                    $civil = "Mlle";
                else
                    $civil = "M.";
            	}
            else $civil = "M.";
}
$this->civil = $civil;

//classe
//------
$this->divcod = $divcod = $this->getfield($ligne, 'classe');
if (empty($this->divcod))
    $this->classe_id = 0;
else
{
    $classe_id = $this->get_create_classe($divcod);
    $this->classe_id = $classe_id;
}

//langues
//-------
$offset = $this->map['lv'];//rang de la langue 1 dans la ligne texte (compté à partir de 0)
//$i indexe le tableau $lv qui est calculé
//$j indexe les champs de $ligne
$i = 1;//il ne peut y avoir "français L.E." dans le fichier texte
$j = $offset;
//print_r($this->map);print "<br />";
while (!empty($ligne[$j]))
{
    $langue_id = $this->trouverlangue($ligne[$offset + $i - 1]);
    if ($langue_id !== false)
    {
        $this->lv[$i] = $langue_id;
        $i++;
    }
    $j++;
}
return true;
}//setbytext
}// classe eleve


/*****************************************************************************/
/*****************************************************************************/
class prof extends qqun {
var $disc_id, $discipline;
var $admin;
var $classes;

function prof() {
parent::qqun('prof');
$this->classes = array();//débute à l'indice 1 ; contiendra des classe_id
}

function setbyobj($obj)	{
//$obj est un résultat de mysql_fetch_array
$this->setcommon($obj);
$this->disc_id = $obj->disc_id;
$this->admin = $obj->admin;
$this->discipline = $this->getgroupvalue();
$rescl = query("SELECT classe_id FROM lien_profs_classes WHERE prof_id=$this->id");
$i = 1;
while (list($cl_id) = mysql_fetch_array($rescl))
    $this->classes[$i++] = $cl_id;
mysql_free_result($rescl);
return $obj;
}

//ces 4 fonctions permettent de traiter de la même façon la classe d'un élève
//et la discipline d'un prof, sous le nom "groupe", ce qui facilite par
//exemple les requêtes (trouver classe/discipline de l'élève/du prof)
//ou les affichages informatifs
function getgroupname() {
return "Discipline";
}
function getgroupfield() {
return "disc_id";
}
function getgroupid() {
return $this->disc_id;
}
function getgroupvalue() {
return getdisc($this->disc_id);
}

function vider() {
parent::vider();
$this->classes = array();
}

//2 fonctions factices appelées par polymorphisme par des fonctions de l'ancêtre qqun.
//ces fonctions n'existent pas pour un prof... sauf lorsqu'on décidera qu'un
//prof peut avoir plusieurs disciplines.
function ajoutgroupe_en_lien() {}
function updategroupe_en_lien(&$old) {}

function ajouteroptions()    {
for ($i = 1; $i <= count($this->classes); $i++)
{
    query("INSERT INTO lien_profs_classes SET prof_id=$this->id, classe_id=" . seci($this->classes[$i]));
}
}


function updateoptions() {
query("DELETE FROM lien_profs_classes WHERE prof_id=$this->id");
$this->ajouteroptions();
}


function insertcompte($champactif)  {
if (!isset($this->mdp))
    $this->mdp = md5($this->dnaiss);
$res = query("INSERT INTO comptes
        SET login='".secs($this->login)."',
            statut='Prof',
            mdp='$this->mdp',
            actif=".seci($champactif)." ,
            nbsessions=0,
            prof_id=$this->id,
            etab_id=$this->etab_id;");
if ($res)
{
    $id = mysql_insert_id();
    query("UPDATE profs SET compte_id=".seci($id )." WHERE prof_id=".seci($this->id));
}
else
    $id = 0;
$this->compte_id = $id;
return $id;
}

//cochercompétences : appelée seulement pour les profs de Melia
function cochercompetences($niveau) {
//on supprime ce qu'on va ajouter de toute façon ensuite afin de ne pas l'avoir en double
//(c'est plus rapide que d'ajouter un par un dans une boucle après vérification)
query("DELETE FROM lpi WHERE prof_id=$this->id");

//transformer par ex. "niv23" en "brevet_no=2 or brevet_no=3"
$where = '';
for ($i = 3; $i < strlen($niveau); $i++)
    $where .= "or brevet_no={$niveau[$i]} ";
$where = substr($where, 3);//enlève le premier "or"
//si prof_id=213, on ajoute tous les couples de la forme (213,x),
//où x vaut chacune des rubriques couverte par le niveau choisi par l'établissement
query("INSERT INTO lpi SELECT $this->id, item_id  FROM items WHERE $where");
}

function couperficelles() {
$id = $this->id;
query("DELETE FROM lien_profs_classes WHERE prof_id=$id;");
query("DELETE FROM lpi WHERE prof_id=$id;");
//on ne supprime pas le compte, c'est nettoyer qui s'en charge tous les 3 ans
}


function maptxt($nomfichier)  {
//Voir fonction maptxt dans la classe eleve
//ordre des champs par défaut d'en-tête (et noms des champs GiBii explicites dans le fichier)
$this->int2gibii = array('ident_nat', 'civil', 'nom', 'prenom', 'discipline',
    					 'dnaiss', 'login', 'mdp', 'classe',
						 999=>'uid');

//mappage sconet vers gibii (1ère lettre en majuscule pour Sconet)
$this->sconet2gibii = array(
			'uid' 		=> 'uid', 			
			'n°' 		=> 'ident_nat',		'n°interne' => 'ident_nat',		'ident_nat'	=> 'ident_nat',
			'civilite' 	=> 'civil', 		'sexe' 		=> 'civil',			'civil'		=> 'civil',		
			'nom' 		=> 'nom', 			'prenom' 	=> 'prenom', 			
			'discipline'=> 'discipline',	'date' 		=> 'dnaiss', 		'dnaiss'	=> 'dnaiss',	
			'login' 	=> 'login',			'mdp' 		=> 'mdp',         	
			'classe' 	=> 'classe',		'division'	=> 'classe',			
			'ine'		=> 'uid'
			);
						
$type_entete = parent::maptxt($nomfichier);
return $type_entete;
}

//***************************************
function setbytxt(&$ligne) {
//affecte un objet à partir d'un tableau $ligne
if (!parent::setbytxt($ligne))  return false;

//pour l'instant, pas d'uid récupérable depuis SCONET... mais on prévoit le futur
$this->uid = $this->getfield($ligne, 'uid');

//civilité
//--------
$civil = ucfirst(strtolower($this->getfield($ligne, 'civil')));
if ($civil != "M." && $civil != "Mlle")
    $civil = "Mme";//statistiquement vrai...
$this->civil = $civil;

//discipline
//----------
$discco = $this->getfield($ligne, 'discipline');
//si inconnue, retourne la valeur par défaut de la table _params_globaux
list($this->disc_id, $this->discipline) = searchdiscipline($discco);

//ajouter les classes du prof
//---------------------------
$valuesreq = "";
        //les classes démarrent au "rang" nommé 'classe'
        $offset = $this->map['classe'];
$i = $offset;
while (!empty($ligne[$i])) {
    $rang = $i - $offset + 1;
    $divcod = trim(strtoupper($ligne[$i]));
    $this->classes[$rang] = $this->get_create_classe($divcod);
    $i++;
	}
    return true;
}//setbytext
}//class
?>