<?php
/**
 * @version $Id$
 * @author Thomas Crespin <thomas.crespin@sesamath.net>
 * @copyright Thomas Crespin 2009-2022
 * 
 * ****************************************************************************************************
 * SACoche <https://sacoche.sesamath.net> - Suivi d’Acquisitions de Compétences
 * © Thomas Crespin pour Sésamath <https://www.sesamath.net> - Tous droits réservés.
 * Logiciel placé sous la licence libre Affero GPL 3 <https://www.gnu.org/licenses/agpl-3.0.html>.
 * ****************************************************************************************************
 * 
 * Ce fichier est une partie de SACoche.
 * 
 * SACoche est un logiciel libre ; vous pouvez le redistribuer ou le modifier suivant les termes 
 * de la “GNU Affero General Public License” telle que publiée par la Free Software Foundation :
 * soit la version 3 de cette licence, soit (à votre gré) toute version ultérieure.
 * 
 * SACoche est distribué dans l’espoir qu’il vous sera utile, mais SANS AUCUNE GARANTIE :
 * sans même la garantie implicite de COMMERCIALISABILITÉ ni d’ADÉQUATION À UN OBJECTIF PARTICULIER.
 * Consultez la Licence Publique Générale GNU Affero pour plus de détails.
 * 
 * Vous devriez avoir reçu une copie de la Licence Publique Générale GNU Affero avec SACoche ;
 * si ce n’est pas le cas, consultez : <http://www.gnu.org/licenses/>.
 * 
 */

if(!defined('SACoche')) {exit('Ce fichier ne peut être appelé directement !');}
if(($_SESSION['SESAMATH_ID']==ID_DEMO)&&($_POST['f_action']!='Afficher_evaluations')&&($_POST['f_action']!='Voir_notes')){Json::end( FALSE , 'Action désactivée pour la démo.' );}

$action     = Clean::post('f_action'    , 'texte');
$eleve_id   = Clean::post('f_eleve'     , 'entier');
$prof_id    = Clean::post('f_prof'      , 'entier');
$date_debut = Clean::post('f_date_debut', 'date_fr');
$date_fin   = Clean::post('f_date_fin'  , 'date_fr');
$all_dates  = Clean::post('f_all_dates' , 'bool');
$devoir_id  = Clean::post('f_devoir'    , 'entier');
$msg_data   = Clean::post('f_msg_data'  , 'texte');
$msg_url    = Clean::post('f_msg_url'   , 'texte');
$msg_autre  = Clean::post('f_msg_autre' , 'texte'); // n’est plus utilisé, mais n’a pas été retiré
$doc_url    = Clean::post('f_doc_url'   , 'url');

$chemin_devoir      = CHEMIN_DOSSIER_DEVOIR.$_SESSION['BASE'].DS;
$url_dossier_devoir = URL_DIR_DEVOIR.$_SESSION['BASE'].'/';

// En cas de manipulation du formulaire (avec les outils de développements intégrés au navigateur ou un module complémentaire)...
// Merci @ Mathieu Degrange <Mathieu-Gilbert.Degrange@ac-nice.fr> d’avoir inspecté le code et signalé ce manquement.

// Pour un élève on surcharge avec les données de session
if($_SESSION['USER_PROFIL_TYPE']=='eleve')
{
  $eleve_id = $_SESSION['USER_ID'];
}

// Pour un parent on vérifie que c’est bien un de ses enfants
if( $eleve_id && ($_SESSION['USER_PROFIL_TYPE']=='parent') )
{
  Outil::verif_enfant_parent( $eleve_id );
}

// Pour un professeur on vérifie que c’est bien un de ses élèves
if( ($_SESSION['USER_PROFIL_TYPE']=='professeur') && ($_SESSION['USER_JOIN_GROUPES']=='config') )
{
  Outil::verif_eleve_prof( $eleve_id );
}

// ////////////////////////////////////////////////////////////////////////////////////////////////////
// Afficher une liste d’évaluations
// ////////////////////////////////////////////////////////////////////////////////////////////////////

if( ($action=='Afficher_evaluations') && $eleve_id && !is_null($prof_id) && $date_debut && $date_fin )
{
  $tab_js_dates = array();
  $tab_js_diagnostic = array();
  // Formater les dates
  $date_debut_sql = To::date_french_to_sql($date_debut);
  $date_fin_sql   = To::date_french_to_sql($date_fin);
  // Vérifier que la date de début est antérieure à la date de fin
  if($date_debut_sql>$date_fin_sql)
  {
    Json::end( FALSE , 'La date de début est postérieure à la date de fin !' );
  }
  // Lister les évaluations
  $DB_TAB = DB_STRUCTURE_ELEVE::DB_lister_devoirs_eleve( $eleve_id , $prof_id , $date_debut_sql , $date_fin_sql , $all_dates , $_SESSION['USER_PROFIL_TYPE'] );
  if(empty($DB_TAB))
  {
    Json::end( FALSE , 'Aucune évaluation trouvée sur la période '.$date_debut.' ~ '.$date_fin.'.' );
  }
  // Récupérer le nb de saisies déjà effectuées par évaluation (ça posait trop de problème dans la requête précédente : saisies comptées plusieurs fois, évaluations sans saisies non retournées...)
  $tab_devoir_id = array();
  foreach($DB_TAB as $DB_ROW)
  {
    $tab_devoir_id[$DB_ROW['devoir_id']] = $DB_ROW['devoir_id'];
  }
  $tab_nb_saisies_effectuees = array_fill_keys($tab_devoir_id,0);
  $DB_TAB2 = DB_STRUCTURE_ELEVE::DB_lister_nb_saisies_par_evaluation( $eleve_id , implode(',',$tab_devoir_id) , $_SESSION['USER_PROFIL_TYPE'] );
  foreach($DB_TAB2 as $DB_ROW)
  {
    $tab_nb_saisies_effectuees[$DB_ROW['devoir_id']] = $DB_ROW['saisies_nombre'];
  }
  foreach($DB_TAB as $DB_ROW)
  {
    $nb_saisies_possibles = $DB_ROW['items_nombre'];
    $date_affich = To::date_sql_to_french($DB_ROW['devoir_date']);
    $lien_copie = ($DB_ROW['jointure_doc_copie']) ? FileSystem::verif_lien_safe($DB_ROW['jointure_doc_copie']) : $DB_ROW['jointure_doc_copie'] ;
    $image_doc_copie  = ($lien_copie) ? '<a href="'.$lien_copie.'" target="_blank" rel="noopener noreferrer" class="no_puce"><img alt="copie élève" src="./_img/document/copie_oui.png"'.infobulle('Copie élève disponible.').'></a>' : '<img alt="copie élève" src="./_img/document/copie_non.png">' ;
    $remplissage_info = ($tab_nb_saisies_effectuees[$DB_ROW['devoir_id']]>=$nb_saisies_possibles) ? 'oui' : ( ($tab_nb_saisies_effectuees[$DB_ROW['devoir_id']]) ? 'partiel' : 'non' ) ;
    $doc_sujet   = ($DB_ROW['jointure_doc_sujet'])   ? $DB_ROW['jointure_doc_sujet']   : ( ($DB_ROW['devoir_doc_sujet'])   ? $DB_ROW['devoir_doc_sujet']   : '' ) ;
    $doc_corrige = ($DB_ROW['jointure_doc_corrige']) ? $DB_ROW['jointure_doc_corrige'] : ( ($DB_ROW['devoir_doc_corrige']) ? $DB_ROW['devoir_doc_corrige'] : '' ) ;
    $q_repartition = ( $DB_ROW['devoir_voir_repartition'] && $tab_nb_saisies_effectuees[$DB_ROW['devoir_id']] ) ? '<q class="voir_repart"'.infobulle('Voir la répartition quantitive des notes saisies à cette évaluation.').'></q>' : '' ;
    $image_sujet   = ($doc_sujet)                ? ' <a href="'.FileSystem::verif_lien_safe($doc_sujet).'" target="_blank" rel="noopener noreferrer" class="no_puce"><img alt="sujet" src="./_img/document/sujet_oui.png"'.infobulle('Voir l’énoncé.').'></a>' : '' ;
    $image_corrige = ($doc_corrige)              ? ' <a href="'.FileSystem::verif_lien_safe($doc_corrige).'" target="_blank" rel="noopener noreferrer" class="no_puce"><img alt="corrigé" src="./_img/document/corrige_oui.png"'.infobulle('Voir le corrigé.').'></a>' : '' ;
    $q_uploader_doc = ($_SESSION['USER_PROFIL_TYPE']=='eleve')
                    ? ( ($lien_copie) ? '<q class="supprimer"'.infobulle('Retirer sa copie (aucune confirmation ne sera demandée).').'></q>' : '<q class="uploader_doc"'.infobulle('Envoyer sa copie.').'></q>' )
                    : '<q class="uploader_doc_non"'.infobulle('Gestion de la copie restreinte à l’élève.').'></q>' ;
    $q_texte       = ($DB_ROW['jointure_texte']) ? '<q class="texte_consulter"'.infobulle('Voir le commentaire écrit.').'></q>' : '' ;
    $q_audio       = ($DB_ROW['jointure_audio']) ? '<q class="audio_ecouter"'.infobulle('Voir le commentaire audio.').'></q>'   : '' ;
    // Afficher une ligne du tableau
    Json::add_row( 'html' , '<tr id="devoir_'.$DB_ROW['devoir_id'].'" data-rempli="'.$remplissage_info.'">' );
    Json::add_row( 'html' ,   '<td>'.html($date_affich).'</td>' );
    Json::add_row( 'html' ,   '<td>'.html(To::texte_genre_identite($DB_ROW['prof_nom'],FALSE,$DB_ROW['prof_prenom'],TRUE,$DB_ROW['prof_genre'])).'</td>' );
    Json::add_row( 'html' ,   '<td>'.html($DB_ROW['devoir_info']).'</td>' );
    Json::add_row( 'html' ,   '<td>'.$image_doc_copie.$q_uploader_doc.'</td>' );
    Json::add_row( 'html' ,   '<td class="hc">'.$remplissage_info.'</td>' );
    Json::add_row( 'html' ,   '<td class="nu">' );
    Json::add_row( 'html' ,     '<q class="voir"'.infobulle('Voir les items et les notes (si saisies).').'></q>' );
    Json::add_row( 'html' ,     $q_repartition );
    Json::add_row( 'html' ,     $image_sujet.$image_corrige );
    Json::add_row( 'html' ,     $q_texte );
    Json::add_row( 'html' ,     $q_audio );
    if($DB_ROW['devoir_autoeval_date']===NULL)
    {
      Json::add_row( 'html' , '' ); // Devoir sans auto-évaluation.
    }
    elseif(!$DB_ROW['devoir_autoeval_en_cours'])
    {
      Json::add_row( 'html' , '<q class="saisir_non"'.infobulle('Auto-évaluation terminée le '.To::datetime_sql_to_french($DB_ROW['devoir_autoeval_date']).'.').'></q>' );
    }
    else
    {
      Json::add_row( 'html' , '<q class="saisir"'.infobulle('Auto-évaluation possible jusqu’au '.To::datetime_sql_to_french($DB_ROW['devoir_autoeval_date']).'.').'></q>' );
      $tab_js_dates[$DB_ROW['devoir_id']] = To::datetime_sql_to_french($DB_ROW['devoir_autoeval_date']);
    }
    $tab_js_diagnostic[$DB_ROW['devoir_id']] = (int)$DB_ROW['devoir_diagnostic'];
    Json::add_row( 'html' ,   '</td>' );
    Json::add_row( 'html' , '</tr>' );
  }
  Json::add_row( 'tab_dates' , json_encode($tab_js_dates) );
  Json::add_row( 'tab_diagnostic' , json_encode($tab_js_diagnostic) );
  Json::end( TRUE );
}

// ////////////////////////////////////////////////////////////////////////////////////////////////////
// Voir les notes saisies à un devoir
// ////////////////////////////////////////////////////////////////////////////////////////////////////

if( ($action=='Voir_notes') && $eleve_id && $devoir_id )
{
  // liste des items
  $DB_TAB_COMP = DB_STRUCTURE_ELEVE::DB_lister_items_devoir_avec_infos_pour_eleves( $devoir_id , $_SESSION['USER_PROFIL_TYPE'] );
  // Normalement, un devoir est toujours lié à au moins un item... sauf si l’item a été supprimé dans le référentiel !
  if(empty($DB_TAB_COMP))
  {
    Json::end( FALSE , 'Ce devoir n’est associé à aucun item !' );
  }
  $tab_liste_item = array_keys($DB_TAB_COMP);
  $liste_item_id = implode(',',$tab_liste_item);
  // Liaisons au socle
  // $DB_TAB_socle2016 = DB_STRUCTURE_REFERENTIEL::DB_recuperer_socle2016_for_items( $liste_item_id );
  // Si l’élève peut formuler des demandes d’évaluations, on doit calculer le score.
  // Du coup, on choisit de récupérer les notes et de calculer les scores pour tous les items.
  $tab_devoirs = array();
  $tab_scores  = array();
  $DB_TAB = DB_STRUCTURE_ELEVE::DB_lister_result_eleve_items( $eleve_id , $liste_item_id , $_SESSION['USER_PROFIL_TYPE'] );
  foreach($DB_TAB as $DB_ROW)
  {
    $tab_devoirs[$DB_ROW['item_id']][] = array('note'=>$DB_ROW['note']);
  }
  // préparer les lignes
  $tab_affich = array();
  $tab_action = array();
  $debut_date = '';
  foreach($tab_liste_item as $item_id)
  {
    $DB_ROW = $DB_TAB_COMP[$item_id][0];
    $item_ref = ($DB_ROW['ref_perso']) ? $DB_ROW['matiere_ref'].'.'.$DB_ROW['ref_perso'] : $DB_ROW['matiere_ref'].'.'.$DB_ROW['ref_auto'] ;
    // Liaison au socle non indiquée car surcharge pour pas grand chose...
    // $socle_nb = !isset($DB_TAB_socle2016[$item_id]) ? 0 : count($DB_TAB_socle2016[$item_id]) ;
    // $texte_s2016 = (!$socle_nb) ? '[–] ' : ( ($socle_nb>1) ? '['.$socle_nb.'c.] ' : '['.($DB_TAB_socle2016[$item_id][0]['composante']/10).'] ' ) ;
    $texte_comm  = ($DB_ROW['item_comm']) ? ' <img src="./_img/etat/comm_oui.png"'.infobulle($DB_ROW['item_comm']).'>' : '' ;
    $texte_lien_avant = ($DB_ROW['item_lien']) ? '<a target="_blank" rel="noopener noreferrer" href="'.html($DB_ROW['item_lien']).'">' : '';
    $texte_lien_apres = ($DB_ROW['item_lien']) ? '</a>' : '';
    $tab_scores[$item_id] = (isset($tab_devoirs[$item_id])) ? OutilBilan::calculer_score( $tab_devoirs[$item_id]  ,$DB_ROW['referentiel_calcul_methode'] , $DB_ROW['referentiel_calcul_limite'] , NULL /*date_sql_debut*/ ) : FALSE ;
    // Pas d’icone de saisie d’évaluation à la volée, cela ne semble pas utile à l’enseignant dans cette interface qui n’est pas un relevé / bilan.
    if(!$DB_ROW['matiere_nb_demandes']) { $icone_action = '<q class="demander_non"'.infobulle('Pas de demande autorisée pour les items de cette matière.').'></q>'; }
    elseif(!$DB_ROW['item_cart'])       { $icone_action = '<q class="demander_non"'.infobulle('Pas de demande autorisée pour cet item précis.').'></q>'; }
    else                                { $icone_action = '<q class="demander_add" data-score="'.( $tab_scores[$item_id] ? $tab_scores[$item_id] : -1 ).'" data-date="'.$debut_date.'"'.infobulle('Ajouter aux demandes d’évaluations.').'></q>'; }
    $tab_affich[$item_id] = '<td>'.html($item_ref).'</td><td>'.$texte_lien_avant.html($DB_ROW['item_nom']).$texte_lien_apres.$texte_comm.'</td>';
    $tab_action[$item_id] = '<td class="nu" data-matiere="'.$DB_ROW['matiere_id'].'" data-item="'.$item_id.'">'.$icone_action.'</td>';
  }
  // Les commentaires texte ou audio ou mémorisation d’auto-éval
  $memo_autoeval = FALSE;
  $commentaire_texte  = '';
  $commentaire_audio  = '';
  $DB_ROW = DB_STRUCTURE_DEVOIR_JOINTURE::DB_recuperer_commentaires($devoir_id,$eleve_id);
  if(!empty($DB_ROW))
  {
    if($DB_ROW['jointure_memo_autoeval'])
    {
      $memo_autoeval = TRUE;
      $tab_autoeval = json_decode( $DB_ROW['jointure_memo_autoeval'] , TRUE );
      foreach($tab_liste_item as $item_id)
      {
        $note = isset($tab_autoeval[$item_id]) ? $tab_autoeval[$item_id] : 'X' ;
        $tab_affich[$item_id] .= (isset($tab_autoeval[$item_id])) ? '<td class="hc">'.Html::note_image($tab_autoeval[$item_id],'','',TRUE /*tri*/).'</td>' : '<td class="hc">-</td>' ;
      }
    }
    if($DB_ROW['jointure_texte'])
    {
      $msg_url = $DB_ROW['jointure_texte'];
      if(strpos($msg_url,URL_DIR_SACOCHE)===0)
      {
        $fichier_chemin = url_to_chemin($msg_url);
        $msg_data = is_file($fichier_chemin) ? file_get_contents($fichier_chemin) : 'Erreur : fichier avec le contenu du commentaire non trouvé.' ;
      }
      else
      {
        $msg_data = cURL::get_contents($msg_url);
      }
      $commentaire_texte = '<h3>Commentaire écrit</h3><textarea rows="10" cols="100" readonly>'.html($msg_data).'</textarea>';
    }
    if($DB_ROW['jointure_audio'])
    {
      $msg_url = $DB_ROW['jointure_audio'];
      if(strpos($msg_url,URL_DIR_SACOCHE)!==0)
      {
        // Violation des directives CSP si on essaye de le lire sur un serveur distant -> on le récupère et le copie localement temporairement
        $msg_data = cURL::get_contents($msg_url);
        $fichier_nom = 'devoir_'.$devoir_id.'_copie_'.$eleve_id.'_audio_copie.mp3';
        FileSystem::ecrire_fichier( CHEMIN_DOSSIER_IMPORT.$fichier_nom , $msg_data );
        $msg_url = URL_DIR_IMPORT.$fichier_nom;
      }
      $commentaire_audio = '<h3>Commentaire audio</h3><audio id="audio_lecture" controls="" src="'.$msg_url.'" class="eleve"><span class="probleme">Votre navigateur est trop ancien, il ne supporte pas la balise [audio] !</span></audio>';
    }
  }
  // récupérer les saisies et les ajouter
  $tab_notes = array();
  $DB_TAB = DB_STRUCTURE_ELEVE::DB_lister_saisies_devoir_eleve( $devoir_id , $eleve_id , $_SESSION['USER_PROFIL_TYPE'] , TRUE /*with_marqueurs*/ );
  foreach($DB_TAB as $DB_ROW)
  {
    $tab_notes[$DB_ROW['item_id']] = $DB_ROW['saisie_note'];
  }
  foreach($tab_liste_item as $item_id)
  {
    $tab_affich[$item_id] .= (isset($tab_notes[$item_id])) ? '<td class="hc">'.Html::note_image($tab_notes[$item_id],'','',TRUE /*tri*/).'</td>' : '<td class="hc">-</td>' ;
  }
  // ajouter les états d’acquisition
  if(Outil::test_user_droit_specifique($_SESSION['DROIT_VOIR_ETAT_ACQUISITION_AVEC_EVALUATION']))
  {
    foreach($tab_liste_item as $item_id)
    {
      $tab_affich[$item_id] .= Html::td_score( $tab_scores[$item_id] , 'score' /*methode_tri*/ , '' /*pourcent*/ );
    }
  }
  // on ajoute dans la dernière colonne les paniers pour les demandes d’évaluation
  foreach($tab_affich as $item_id => $tds)
  {
    $tab_affich[$item_id] .= $tab_action[$item_id];
  }
  $affichage = '<tr>'.implode('</tr><tr>',$tab_affich).'</tr>';
  // la légende, qui peut être personnalisée (codes AB, NN, etc.)
  $score_legende  = (Outil::test_user_droit_specifique($_SESSION['DROIT_VOIR_ETAT_ACQUISITION_AVEC_EVALUATION'])) ? TRUE : FALSE ;
  $legende = Html::legende( array( 'codes_notation'=>TRUE , 'score_bilan'=>$score_legende ) );
  // Retour
  Json::add_tab( array(
    'lignes'        => $affichage ,
    'legende'       => $legende ,
    'memo_autoeval' => $memo_autoeval ,
    'texte'         => $commentaire_texte ,
    'audio'         => $commentaire_audio ,
  ) );
  Json::end( TRUE );
}

// ////////////////////////////////////////////////////////////////////////////////////////////////////
// Voir la répartition quantitive des notes saisies
// ////////////////////////////////////////////////////////////////////////////////////////////////////

if( ($action=='Voir_repartition') && $eleve_id && $devoir_id )
{
  $tab_item = array();
  $tab_note = array();
  $tab_eleve_note = array();
  $tab_repartition = array();
  $tab_init = array();
  foreach( $_SESSION['NOTE_ACTIF'] as $note_id )
  {
    $tab_init[$note_id] = 0;
  }
  $tab_init['X'] = 0;
  // liste des items
  $DB_TAB_ITEM = DB_STRUCTURE_ELEVE::DB_lister_items_devoir_avec_infos_pour_eleves( $devoir_id , $_SESSION['USER_PROFIL_TYPE'] );
  // Normalement, un devoir est toujours lié à au moins un item... sauf si l’item a été supprimé dans le référentiel !
  if(empty($DB_TAB_ITEM))
  {
    Json::end( FALSE , 'Ce devoir n’est associé à aucun item !' );
  }
  foreach($DB_TAB_ITEM as $item_id => $DB_TAB)
  {
    $DB_ROW = $DB_TAB[0];
    $item_ref = ($DB_ROW['ref_perso']) ? $DB_ROW['matiere_ref'].'.'.$DB_ROW['ref_perso'] : $DB_ROW['matiere_ref'].'.'.$DB_ROW['ref_auto'] ;
    $texte_lien_avant = ($DB_ROW['item_lien']) ? '<a target="_blank" rel="noopener noreferrer" href="'.html($DB_ROW['item_lien']).'">' : '';
    $texte_lien_apres = ($DB_ROW['item_lien']) ? '</a>' : '';
    $tab_item[$item_id] = '<th><b>'.html($item_ref).'</b><br>'.$texte_lien_avant.html($DB_ROW['item_nom']).$texte_lien_apres.'</th>';
    $tab_repartition[$item_id] = $tab_init;
  }
  // liste de saisies
  $DB_TAB = DB_STRUCTURE_PROFESSEUR::DB_lister_devoir_saisies( $devoir_id , FALSE /*with_marqueurs*/ );
  foreach($DB_TAB as $DB_ROW)
  {
    if(isset($tab_item[$DB_ROW['item_id']]))
    {
      $note = in_array($DB_ROW['saisie_note'],$_SESSION['NOTE_ACTIF']) ? $DB_ROW['saisie_note'] : 'X' ;
      $tab_note[$note] = $note;
      $tab_repartition[$DB_ROW['item_id']][$note]++;
      if($DB_ROW['eleve_id']==$eleve_id)
      {
        $tab_eleve_note[$DB_ROW['item_id']] = $note;
      }
    }
  }
  if(empty($tab_note))
  {
    Json::end( FALSE , 'Les items de ce devoir n’ont pas encore été évalués !' );
  }
  if(empty($tab_eleve_note))
  {
    Json::end( FALSE , 'Vous n’avez pas été évalué sur ce devoir (pour l’instant) !' );
  }
  // On prépare la sortie
  $tab_tr = array();
  $tab_tr[0] = '<tr><th class="nu"></th>';
  foreach($tab_init as $note_id => $nb)
  {
    if(isset($tab_note[$note_id]))
    {
      $tab_tr[0] .= ($note_id!='X') ? '<th>'.Html::note_image($note_id,'','',FALSE).'</th>' : '<th>Autre</th>' ;
    }
    else
    {
      unset($tab_init[$note_id]);
    }
  }
  $tab_tr[0] .= '</tr>' ;
  foreach($tab_item as $item_id => $item_th)
  {
    if(isset($tab_eleve_note[$item_id]))
    {
      $tab_tr[$item_id] = '<tr>'.$item_th;
      foreach($tab_init as $note_id => $nb)
      {
        $nb_total = array_sum($tab_repartition[$item_id]);
        $valeur = round( 100 * $tab_repartition[$item_id][$note_id] / $nb_total );
        $texte = ($valeur) ? $valeur.'%' : '-' ;
        $fond_eleve = ( $tab_eleve_note[$item_id] == $note_id ) ? ';background-color:yellow' : '' ;
        $tab_tr[$item_id] .= '<td style="font-size:'.(75+$valeur).'%'.$fond_eleve.'">'.$texte.'</td>';
      }
      $tab_tr[$item_id] .= '</tr>';
    }
  }
  // On assemble la sortie
  $retour = '<thead>'.array_shift($tab_tr).'</tr></thead><tbody>';
  foreach($tab_tr as $tr)
  {
    $retour .= $tr;
  }
  $retour .= '</tbody>';
  Json::end( TRUE , $retour );
}

// ////////////////////////////////////////////////////////////////////////////////////////////////////
// Saisir les notes d’un devoir (auto-évaluation)
// ////////////////////////////////////////////////////////////////////////////////////////////////////

if( ($action=='Saisir_notes') && $eleve_id && $devoir_id )
{
  // liste des items
  $DB_TAB_COMP = DB_STRUCTURE_ELEVE::DB_lister_items_devoir_avec_infos_pour_eleves( $devoir_id , $_SESSION['USER_PROFIL_TYPE'] );
  // Normalement, un devoir est toujours lié à au moins un item... sauf si l’item a été supprimé dans le référentiel !
  if(empty($DB_TAB_COMP))
  {
    Json::end( FALSE , 'Ce devoir n’est associé à aucun item !' );
  }
  // Pas de demandes d’évaluations formulées depuis ce formulaire, pas de score affiché non plus
  $tab_liste_item = array_keys($DB_TAB_COMP);
  $liste_item_id = implode(',',$tab_liste_item);
  // Liaisons au socle
  // $DB_TAB_socle2016 = DB_STRUCTURE_REFERENTIEL::DB_recuperer_socle2016_for_items( $liste_item_id );
  // boutons radio
  $tab_radio_boutons = array();
  $tab_notes = array_merge( $_SESSION['NOTE_ACTIF'] , array( 'X' , 'AB' ) ); // , 'NN' , 'NE' , 'NF' , 'NR' , 'DI' , 'PA'
  foreach($tab_notes as $note)
  {
    $tab_radio_boutons[] = '<label for="item_X_'.$note.'"><input type="radio" id="item_X_'.$note.'" name="item_X" value="'.$note.'"><br><img alt="'.$note.'" src="'.Html::note_src($note).'"></label>';
  }
  $radio_boutons = '<td class="hc">'.implode('</td><td class="hc">',$tab_radio_boutons).'</td>';
  // récupérer les saisies
  $tab_radio = array();
  $DB_TAB = DB_STRUCTURE_ELEVE::DB_lister_saisies_devoir_eleve( $devoir_id , $eleve_id , $_SESSION['USER_PROFIL_TYPE'] , FALSE /*with_marqueurs*/ );
  foreach($DB_TAB as $DB_ROW)
  {
    $tab_radio[$DB_ROW['item_id']] = str_replace( 'value="'.$DB_ROW['saisie_note'].'"' , 'value="'.$DB_ROW['saisie_note'].'" checked' , $radio_boutons );
  }
  // récupérer les commentaires texte ou audio ou mémorisation d’auto-éval
  $msg_memo_autoeval = '';
  $msg_texte_url     = '';
  $msg_texte_data    = '';
  $msg_audio_autre   = 'non';
  $DB_ROW = DB_STRUCTURE_DEVOIR_JOINTURE::DB_recuperer_commentaires($devoir_id,$eleve_id);
  if(!empty($DB_ROW))
  {
    if($DB_ROW['jointure_memo_autoeval'])
    {
      $msg_memo_autoeval = '<h3>Mémorisation auto-évaluation</h3>';
      $msg_memo_autoeval.= '<div class="legende">';
      $tab_autoeval = json_decode( $DB_ROW['jointure_memo_autoeval'] , TRUE );
      foreach($tab_liste_item as $item_id)
      {
        $note = isset($tab_autoeval[$item_id]) ? $tab_autoeval[$item_id] : 'X' ;
        $msg_memo_autoeval .= '<img alt="'.$note.'" src="'.Html::note_src($note).'">';
      }
      $msg_memo_autoeval.= '</div>';
    }
    if($DB_ROW['jointure_texte'])
    {
      $msg_texte_url = $DB_ROW['jointure_texte'];
      if(strpos($msg_texte_url,URL_DIR_SACOCHE)===0)
      {
        $fichier_chemin = url_to_chemin($msg_texte_url);
        $msg_texte_data = is_file($fichier_chemin) ? file_get_contents($fichier_chemin) : 'Erreur : fichier avec le contenu du commentaire non trouvé.' ;
      }
      else
      {
        $msg_texte_data = cURL::get_contents($msg_texte_url);
      }
    }
    if($DB_ROW['jointure_audio'])
    {
      $msg_audio_autre = 'oui';
    }
  }
  // lignes du tableau à retourner
  $lignes = '';
  foreach($tab_liste_item as $item_id)
  {
    $DB_ROW = $DB_TAB_COMP[$item_id][0];
    $item_ref = ($DB_ROW['ref_perso']) ? $DB_ROW['matiere_ref'].'.'.$DB_ROW['ref_perso'] : $DB_ROW['matiere_ref'].'.'.$DB_ROW['ref_auto'] ;
    // Liaison au socle non indiquée car surcharge pour pas grand chose...
    // $socle_nb = !isset($DB_TAB_socle2016[$item_id]) ? 0 : count($DB_TAB_socle2016[$item_id]) ;
    // $texte_s2016 = (!$socle_nb) ? '[–] ' : ( ($socle_nb>1) ? '['.$socle_nb.'c.] ' : '['.($DB_TAB_socle2016[$item_id][0]['composante']/10).'] ' ) ;
    $texte_comm  = ($DB_ROW['item_comm']) ? ' <img src="./_img/etat/comm_oui.png"'.infobulle($DB_ROW['item_comm']).'>' : '' ;
    $texte_lien_avant = ($DB_ROW['item_lien']) ? '<a target="_blank" rel="noopener noreferrer" href="'.html($DB_ROW['item_lien']).'">' : '';
    $texte_lien_apres = ($DB_ROW['item_lien']) ? '</a>' : '';
    $boutons = (isset($tab_radio[$item_id])) ? $tab_radio[$item_id] : str_replace( 'value="X"' , 'value="X" checked' , $radio_boutons ) ;
    $boutons = str_replace( 'item_X' , 'item_'.$item_id , $boutons );
    $lignes .= '<tr>'.$boutons.'<td>'.html($item_ref).'<br>'.$texte_lien_avant.html($DB_ROW['item_nom']).$texte_lien_apres.$texte_comm.'</td></tr>';
  }
  // Retour
  Json::add_tab( array(
    'lignes'        => $lignes ,
    'audio_autre'   => $msg_audio_autre ,
    'memo_autoeval' => $msg_memo_autoeval ,
    'texte_url'     => $msg_texte_url ,
    'texte_data'    => $msg_texte_data ,
  ) );
  Json::end( TRUE );
}

// ////////////////////////////////////////////////////////////////////////////////////////////////////
// Enregistrer des notes saisies (auto-évaluation)
// ////////////////////////////////////////////////////////////////////////////////////////////////////

if( ($action=='Enregistrer_saisies') && $devoir_id && in_array($msg_autre,array('oui','non')) )
{
  if($_SESSION['USER_PROFIL_TYPE']!='eleve')
  {
    Json::end( FALSE , 'Appel illicite.' );
  }
  // On récupère les informations associées à ce devoir et on vérifie que l’élève est en droit de s’y auto-évaluer.
  $DB_ROW = DB_STRUCTURE_ELEVE::DB_recuperer_devoir_infos($devoir_id);
  if(empty($DB_ROW))
  {
    Json::end( FALSE , 'Devoir introuvable !' );
  }
  if($DB_ROW['devoir_autoeval_date']===NULL)
  {
    Json::end( FALSE , 'Devoir sans auto-évaluation !' );
  }
  if(!$DB_ROW['devoir_autoeval_en_cours'])
  {
    Json::end( FALSE , 'Auto-évaluation terminée le '.To::datetime_sql_to_french($DB_ROW['devoir_autoeval_date']).' !' );
  }
  $devoir_proprio_id  = $DB_ROW['proprio_id'];
  $devoir_date_sql    = $DB_ROW['devoir_date'];
  $devoir_description = $DB_ROW['devoir_info'];
  // Récupérer les items du devoir afin d’éviter des valeurs truquées ou le problème d’élève retiré entre l’affichage et la saisie (c’est arrivé).
  $DB_TAB_COMP = DB_STRUCTURE_ELEVE::DB_lister_items_devoir_avec_infos_pour_eleves( $devoir_id , $_SESSION['USER_PROFIL_TYPE'] );
  // Tout est transmis : il faut comparer avec le contenu de la base pour ne mettre à jour que ce dont il y a besoin
  // On récupère les notes transmises dans $tab_post
  $tab_post = array();
  foreach($_POST as $key => $val)
  {
    if( (substr($key,0,5)=='item_') && isset($DB_TAB_COMP[substr($key,5)]) )
    {
      $item_id = (int)substr($key,5);
      $note    = $val;
      $tab_post[$item_id] = $note;
    }
  }
  if(!count($tab_post))
  {
    Json::end( FALSE , 'Aucune saisie récupérée !' );
  }
  // On mémorise l’auto-évaluation (avant de perdre les infos)
  DB_STRUCTURE_DEVOIR_JOINTURE::DB_remplacer_objet( $devoir_id , $_SESSION['USER_ID'] , 'memo_autoeval' , json_encode($tab_post) );
  // On recupère le contenu de la base déjà enregistré pour le comparer ; on remplit au fur et à mesure $tab_nouveau_modifier / $tab_nouveau_supprimer
  // $tab_demande_archiver sert à archiver des demandes d’élèves dont on met une note.
  $tab_nouveau_modifier  = array();
  $tab_nouveau_supprimer = array();
  $tab_demande_archiver  = array();
  $DB_TAB = DB_STRUCTURE_ELEVE::DB_lister_saisies_devoir_eleve( $devoir_id , $_SESSION['USER_ID'] , $_SESSION['USER_PROFIL_TYPE'] , TRUE /*with_marqueurs*/ );
  foreach($DB_TAB as $DB_ROW)
  {
    $item_id = (int)$DB_ROW['item_id'];
    if( isset($tab_post[$item_id]) && isset($DB_TAB_COMP[$item_id]) ) // Test nécessaire si élève ou item évalués dans ce devoir, mais retiré depuis (donc non transmis dans la nouvelle saisie, mais à conserver).
    {
      if($tab_post[$item_id]!=$DB_ROW['saisie_note'])
      {
        if($tab_post[$item_id]=='X')
        {
          // valeur de la base à supprimer
          $tab_nouveau_supprimer[$item_id] = $item_id;
        }
        else
        {
          // valeur de la base à modifier
          $tab_nouveau_modifier[$item_id] = $tab_post[$item_id];
          if($DB_ROW['saisie_note']=='PA')
          {
            // demande d’évaluation à supprimer
            $tab_demande_archiver[$item_id] = $item_id;
          }
        }
      }
      unset($tab_post[$item_id]);
    }
  }
  // Il reste dans $tab_post les données à ajouter (mises dans $tab_nouveau_ajouter) et les données qui ne servent pas (non enregistrées et non saisies)
  $tab_nouveau_ajouter = array_filter($tab_post,'sans_rien');
  //
  // Il n’y a plus qu’à mettre à jour la base
  //
  // L’information associée à la note comporte le nom de l’évaluation + celui du professeur (c’est une information statique, conservée sur plusieurs années)
  $info = $devoir_description.' ('.To::texte_genre_identite($_SESSION['USER_NOM'],FALSE,$_SESSION['USER_PRENOM'],TRUE,$_SESSION['USER_GENRE']).')';
  foreach($tab_nouveau_ajouter as $item_id => $note)
  {
    DB_STRUCTURE_PROFESSEUR::DB_ajouter_saisie( $devoir_proprio_id , $_SESSION['USER_ID'] , $devoir_id , $item_id , $devoir_date_sql , $note , $info , NULL /*item_date_visible_sql*/ );
  }
  foreach($tab_nouveau_modifier as $item_id => $note)
  {
    DB_STRUCTURE_PROFESSEUR::DB_modifier_saisie( $devoir_proprio_id , $_SESSION['USER_ID'] , $devoir_id , $item_id , $note , $info );
  }
  foreach($tab_nouveau_supprimer as $item_id)
  {
    DB_STRUCTURE_PROFESSEUR::DB_supprimer_saisie( $_SESSION['USER_ID'] , $devoir_id , $item_id );
  }
  foreach($tab_demande_archiver as $item_id)
  {
    DB_STRUCTURE_DEMANDE::DB_archiver_demande_precise_eleve_item( $_SESSION['USER_ID'] , $item_id );
  }
  // Ajout aux flux RSS des profs concernés
  $tab_profs_rss = array_merge( array($devoir_proprio_id) , DB_STRUCTURE_ELEVE::DB_lister_devoir_profs_droit_saisie($devoir_id) );
  $titre = 'Autoévaluation effectuée par '.To::texte_genre_identite($_SESSION['USER_NOM'],FALSE,$_SESSION['USER_PRENOM'],TRUE);
  $texte = $_SESSION['USER_PRENOM'].' '.$_SESSION['USER_NOM'].' s’auto-évalue sur le devoir "'.$devoir_description.'".'."\r\n";
  $texte.= ($msg_data) ? 'Commentaire :'."\r\n".$msg_data."\r\n" : 'Pas de commentaire saisi.'."\r\n" ;
  $guid  = 'autoeval_'.$devoir_id.'_'.$_SESSION['USER_ID'].'_'.$_SERVER['REQUEST_TIME']; // obligé d’ajouter un time pour unicité au cas où un élève valide 2x l’autoévaluation
  foreach($tab_profs_rss as $prof_id)
  {
    RSS::modifier_fichier_prof($prof_id,$titre,$texte,$guid);
  }
  // Notifications (rendues visibles ultérieurement) ; on récupère des données conçues pour le flux RSS ($texte , $tab_profs_rss)
  $abonnement_ref = 'devoir_autoevaluation_eleve';
  $listing_profs = implode(',',$tab_profs_rss);
  if($listing_profs)
  {
    $listing_abonnes = DB_STRUCTURE_NOTIFICATION::DB_lister_destinataires_listing_id( $abonnement_ref , $listing_profs );
    if($listing_abonnes)
    {
      $notification_contenu = $texte;
      $tab_abonnes = explode(',',$listing_abonnes);
      foreach($tab_abonnes as $abonne_id)
      {
        DB_STRUCTURE_NOTIFICATION::DB_modifier_log_attente( $abonne_id , $abonnement_ref , 0 , NULL , $notification_contenu , 'compléter' , TRUE /*sep*/ );
      }
    }
  }
  //
  // On passe maintenant au commentaire texte
  //
  // Supprimer un éventuel fichier précédent
  if( $msg_url && (mb_strpos($msg_url,$url_dossier_devoir)===0) )
  {
    // Il peut ne pas être présent sur le serveur en cas de restauration de base ailleurs, etc.
    FileSystem::supprimer_fichier( url_to_chemin($msg_url) , TRUE /*verif_exist*/ );
  }
  // Mise à jour dans la base
  if($msg_data)
  {
    $fichier_nom = 'devoir_'.$devoir_id.'_copie_'.$_SESSION['USER_ID'].'_'.'texte'.'_'.$_SERVER['REQUEST_TIME'].'.'.'txt'; // pas besoin de le rendre inaccessible -> FileSystem::generer_fin_nom_fichier__date_et_alea() inutilement lourd
    DB_STRUCTURE_DEVOIR_JOINTURE::DB_remplacer_objet( $devoir_id , $_SESSION['USER_ID'] , 'texte' , $url_dossier_devoir.$fichier_nom );
    // et enregistrement du fichier
    FileSystem::creer_sous_dossier_etabl_si_besoin( $chemin_devoir );
    FileSystem::ecrire_fichier( $chemin_devoir.$fichier_nom , $msg_data );
  }
  else
  {
    DB_STRUCTURE_DEVOIR_JOINTURE::DB_remplacer_objet( $devoir_id , $_SESSION['USER_ID'] , 'texte' , NULL );
  }
  // Terminé
  Json::end( TRUE );
}

// ////////////////////////////////////////////////////////////////////////////////////////////////////
// Uploader une copie d’élève
// ////////////////////////////////////////////////////////////////////////////////////////////////////

if( ($action=='envoyer_copie') && $devoir_id )
{
  if($_SESSION['USER_PROFIL_TYPE']!='eleve')
  {
    Json::end( FALSE , 'Appel illicite.' );
  }
  $doc_objet = 'copie';
  // Récupération du fichier
  FileSystem::creer_sous_dossier_etabl_si_besoin( $chemin_devoir );
  $fichier_nom = 'devoir_'.$devoir_id.'_eleve_'.$_SESSION['USER_ID'].'_'.$doc_objet.'_'.$_SERVER['REQUEST_TIME'].'.<EXT>'; // pas besoin de le rendre inaccessible -> FileSystem::generer_fin_nom_fichier__date_et_alea() inutilement lourd
  $result = FileSystem::recuperer_upload( $chemin_devoir /*fichier_chemin*/ , $fichier_nom /*fichier_nom*/ , NULL /*tab_extensions_autorisees*/ , FileSystem::$tab_extensions_interdites , FICHIER_TAILLE_MAX /*taille_maxi*/ , NULL /*filename_in_zip*/ );
  if($result!==TRUE)
  {
    Json::end( FALSE , $result );
  }
  // Mise à jour dans la base
  DB_STRUCTURE_DEVOIR_JOINTURE::DB_remplacer_objet( $devoir_id , $_SESSION['USER_ID'] , 'doc_'.$doc_objet , $url_dossier_devoir.FileSystem::$file_saved_name );
  // Retour
  Json::add_tab( array(
    'devoir_id' => $devoir_id ,
    'url'       => FileSystem::verif_lien_safe($url_dossier_devoir.FileSystem::$file_saved_name) ,
  ) );
  Json::end( TRUE );
}

// ////////////////////////////////////////////////////////////////////////////////////////////////////
// Retirer un sujet ou un corrigé d’évaluation (groupe ou éléve précis)
// ////////////////////////////////////////////////////////////////////////////////////////////////////

if( ($action=='retirer_copie') && $devoir_id && $doc_url )
{
  if($_SESSION['USER_PROFIL_TYPE']!='eleve')
  {
    Json::end( FALSE , 'Appel illicite.' );
  }
  $doc_objet = 'copie';
  // Suppression du fichier, uniquement si ce n’est pas un lien externe ou vers un devoir d’un autre établissement
  if(mb_strpos($doc_url,$url_dossier_devoir)===0)
  {
    // Il peut ne pas être présent sur le serveur en cas de restauration de base ailleurs, etc.
    FileSystem::supprimer_fichier( url_to_chemin($doc_url) , TRUE /*verif_exist*/ );
  }
  // Mise à jour dans la base
  DB_STRUCTURE_DEVOIR_JOINTURE::DB_remplacer_objet( $devoir_id , $_SESSION['USER_ID'] , 'doc_'.$doc_objet , NULL );
  // Retour
  Json::end( TRUE );
}

// ////////////////////////////////////////////////////////////////////////////////////////////////////
// Il se peut que rien n’ait été récupéré à cause de l’upload d’un fichier trop lourd
// ////////////////////////////////////////////////////////////////////////////////////////////////////

if(empty($_POST))
{
  Json::end( FALSE , 'Aucune donnée reçue ! Fichier trop lourd ? '.InfoServeur::minimum_limitations_upload() );
}

// ////////////////////////////////////////////////////////////////////////////////////////////////////
// On ne devrait pas en arriver là
// ////////////////////////////////////////////////////////////////////////////////////////////////////

Json::end( FALSE , 'Erreur avec les données transmises !<br>'.pathinfo(__FILE__,PATHINFO_FILENAME).':'.__LINE__ );

?>
