<?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/>.
 *
 */
 
// Extension de classe qui étend DB (pour permettre l’autoload)

// Ces méthodes ne concernent qu’une base STRUCTURE.
// Ces méthodes ne concernent que les professeurs.

class DB_STRUCTURE_PROFESSEUR
{

/**
 * recuperer_devoir_ponctuel_prof_by_date
 *
 * @param int    $prof_id
 * @param string $date_sql
 * @param string $description
 * @return array
 */
public static function DB_recuperer_devoir_ponctuel_prof_by_date( $prof_id , $date_sql , $description )
{
  $DB_SQL = 'SELECT devoir_id, groupe_id '
          . 'FROM sacoche_devoir '
          . 'LEFT JOIN sacoche_groupe USING (groupe_id) '
          . 'WHERE proprio_id=:proprio_id AND groupe_type=:type4 AND devoir_date>=:date_sql AND devoir_info=:description ' 
          . 'LIMIT 1';
  $DB_VAR = array(
    ':proprio_id'  => $prof_id,
    ':type4'       => 'eval',
    ':date_sql'    => $date_sql,
    ':description' => $description,
  );
  return DB::queryRow(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * recuperer_devoir_prorietaire_id
 *
 * @param int $devoir_id
 * @return int
 */
public static function DB_recuperer_devoir_proprietaire_id($devoir_id)
{
  $DB_SQL = 'SELECT proprio_id '
          . 'FROM sacoche_devoir '
          . 'WHERE devoir_id=:devoir_id ';
  $DB_VAR = array( ':devoir_id' => $devoir_id );
  return (int)DB::queryOne(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * recuperer_devoir_prorietaire_identite
 *
 * @param int $devoir_id
 * @return string
 */
public static function DB_recuperer_devoir_proprietaire_identite($devoir_id)
{
  $DB_SQL = 'SELECT user_genre, user_nom, user_prenom '
          . 'FROM sacoche_devoir '
          . 'LEFT JOIN sacoche_user ON sacoche_devoir.proprio_id=sacoche_user.user_id '
          . 'WHERE devoir_id=:devoir_id ';
  $DB_VAR = array( ':devoir_id' => $devoir_id );
  return DB::queryRow(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * Retourner qq infos sur un élève
 *
 * @param int $eleve_id
 * @return string
 */
public static function DB_recuperer_eleve_infos( $eleve_id )
{
  $DB_SQL = 'SELECT user_nom, user_prenom, eleve_classe_id, '
          . 'GROUP_CONCAT(DISTINCT groupe_id SEPARATOR ",") AS listing_groupes '
          . 'FROM sacoche_user '
          . 'LEFT JOIN sacoche_jointure_user_groupe USING (user_id) '
          . 'WHERE user_id=:user_id '
          . 'GROUP BY user_id ';
  $DB_VAR = array( ':user_id' => $eleve_id );
  return DB::queryRow(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * Retourner une chaine avec les id des élèves d’une classe ou d’un groupe
 *
 * @param string $groupe_type    "classe" | "groupe"
 * @param int    $groupe_id      id du niveau ou de la classe ou du groupe
 * @return string
 */
public static function DB_recuperer_listing_eleves_id( $groupe_type , $groupe_id )
{
  if($groupe_type=='classe')
  {
    $join_groupe = '';
    $where_groupe = 'AND eleve_classe_id=:groupe ';
  }
  else
  {
    $join_groupe = 'LEFT JOIN sacoche_jointure_user_groupe USING (user_id) ';
    $where_groupe = 'AND sacoche_jointure_user_groupe.groupe_id=:groupe ';
  }
  $DB_SQL = 'SELECT CONVERT( GROUP_CONCAT(user_id SEPARATOR ",") , CHAR) AS identifiants '
          . 'FROM sacoche_user '
          . 'LEFT JOIN sacoche_user_profil USING (user_profil_sigle) '
          . $join_groupe
          . 'WHERE user_profil_type=:profil_type AND user_sortie_date>NOW() '.$where_groupe;
  $DB_VAR = array(
    ':profil_type' => 'eleve',
    ':groupe'      => $groupe_id,
  );
  return DB::queryOne(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * Retourner une chaine avec les id des enseignants d’une classe ou d’un groupe
 *
 * @param string $groupe_type   "classe" | "groupe"
 * @param int    $groupe_id     id du niveau ou de la classe ou du groupe
 * @return string
 */
public static function DB_recuperer_listing_professeurs_id( $groupe_type , $groupe_id )
{
  $DB_SQL = 'SELECT CONVERT( GROUP_CONCAT(user_id SEPARATOR ",") , CHAR) AS identifiants '
          . 'FROM sacoche_jointure_user_groupe '
          . 'LEFT JOIN sacoche_user USING (user_id) '
          . 'LEFT JOIN sacoche_user_profil USING (user_profil_sigle) '
          . 'WHERE user_profil_type=:profil_type AND user_sortie_date>NOW() AND groupe_id=:groupe ';
  $DB_VAR = array(
    ':profil_type' => 'professeur',
    ':groupe'      => $groupe_id,
  );
  return DB::queryOne(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * Tester si une note déjà saisie peut être modifiée à la volée par un enseignant.
 * En particulier on restreint à l’auteur de cette saisie et à l’année scolaire en cours.
 *
 * @param int    $eleve_id
 * @param int    $devoir_id
 * @param int    $item_id
 * @return int
 */
public static function DB_tester_note_modifiable( $eleve_id , $devoir_id , $item_id )
{
  $DB_SQL = 'SELECT saisie_note '
          . 'FROM sacoche_saisie '
          . 'WHERE eleve_id=:eleve_id AND devoir_id=:devoir_id AND item_id=:item_id AND prof_id=:prof_id AND saisie_date>=:date_start';
  $DB_VAR = array(
    ':eleve_id'   => $eleve_id,
    ':devoir_id'  => $devoir_id,
    ':item_id'    => $item_id,
    ':prof_id'    => $_SESSION['USER_ID'],
    ':date_start' => To::jour_debut_annee_scolaire('sql'),
  );
  return DB::queryOne(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * tester_devoir_ponctuel_prof_by_ids
 *
 * @param int    $devoir_id
 * @param int    $prof_id
 * @param int    $groupe_id
 * @param string $description
 * @return bool
 */
public static function DB_tester_devoir_ponctuel_prof_by_ids( $devoir_id , $prof_id , $groupe_id , $description )
{
  $DB_SQL = 'SELECT 1 '
          . 'FROM sacoche_devoir '
          . 'LEFT JOIN sacoche_groupe USING (groupe_id) '
          . 'WHERE devoir_id=:devoir_id AND proprio_id=:proprio_id AND groupe_id=:groupe_id AND groupe_type=:type4 AND devoir_info=:description ' 
          . 'LIMIT 1';
  $DB_VAR = array(
    ':devoir_id'   => $devoir_id,
    ':proprio_id'  => $prof_id,
    ':groupe_id'   => $groupe_id,
    ':type4'       => 'eval',
    ':description' => $description,
  );
  return (bool)DB::queryOne(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * compter_demandes_evaluation
 *
 * @param int  $prof_id
 * @param string $user_join_groupes
 * @return array
 */
public static function DB_compter_demandes_evaluation( $prof_id , $user_join_groupes )
{
  $listing_eleves_id = DB_STRUCTURE_PROFESSEUR::DB_lister_ids_eleves_professeur( $prof_id , $user_join_groupes , 'string' /*format_retour*/ );
  if(!$listing_eleves_id)
  {
    return array();
  }
  $DB_SQL = 'SELECT demande_statut, COUNT(demande_id) AS nombre '
          . 'FROM sacoche_demande '
          . 'LEFT JOIN sacoche_jointure_user_matiere ON sacoche_demande.matiere_id=sacoche_jointure_user_matiere.matiere_id '
          . 'WHERE eleve_id IN('.$listing_eleves_id.') AND demande_statut != :not_statut AND prof_id IN(0,:prof_id) AND sacoche_jointure_user_matiere.user_id=:user_id '
          . 'GROUP BY demande_statut ';
  $DB_VAR = array(
    ':prof_id'    => $_SESSION['USER_ID'],
    ':user_id'    => $_SESSION['USER_ID'],
    ':not_statut' => 'done',
  );
  return DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR , TRUE , TRUE);
}

/**
 * _compter_devoir_matieres_non_experimentales
 *
 * @param int $devoir_id
 * @return int
 */
public static function DB_compter_devoir_matieres_non_experimentales( $devoir_id )
{
  $DB_SQL = 'SELECT COUNT( DISTINCT matiere_id ) AS nombre '
          . 'FROM sacoche_jointure_devoir_item '
          . 'LEFT JOIN sacoche_referentiel_item USING(item_id) '
          . 'LEFT JOIN sacoche_referentiel_theme USING(theme_id) '
          . 'LEFT JOIN sacoche_referentiel_domaine USING(domaine_id) '
          . 'LEFT JOIN sacoche_matiere USING(matiere_id) '
          . 'WHERE devoir_id=:devoir_id AND matiere_experimentale = 0 ';
  $DB_VAR = array( ':devoir_id' => $devoir_id );
  return DB::queryOne(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR );
}

/**
 * Retourner les niveaux et matières des référentiels auxquels un professeur a accès.
 *
 * @param int   $prof_id
 * @param bool  $without_matiere_experimentale   si TRUE, ne pas récupérer les matières expérimentales
 * @return array
 */
public static function DB_lister_matieres_niveaux_referentiels_professeur( $prof_id , $without_matiere_experimentale )
{
  $where_expe = ($without_matiere_experimentale) ? 'AND matiere_experimentale = 0 ' : '' ;
  $DB_SQL = 'SELECT matiere_id, matiere_ref, matiere_nom, matiere_experimentale, niveau_id, niveau_nom, jointure_coord '
          . 'FROM sacoche_referentiel '
          . 'LEFT JOIN sacoche_jointure_user_matiere USING (matiere_id) '
          . 'LEFT JOIN sacoche_matiere USING (matiere_id) '
          . 'LEFT JOIN sacoche_niveau USING (niveau_id) '
          . 'WHERE user_id=:user_id AND matiere_active=1 AND niveau_actif=1 '.$where_expe
          . 'ORDER BY matiere_nom ASC, niveau_ordre ASC';
  $DB_VAR = array( ':user_id' => $prof_id );
  return DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * lister_matieres_professeur_infos_referentiel
 *
 * @param int $prof_id
 * @return array|string
 */
public static function DB_lister_matieres_professeur_infos_referentiel($prof_id)
{
  $DB_SQL = 'SELECT matiere_id, matiere_nom, matiere_nb_demandes, matiere_experimentale, jointure_coord '
          . 'FROM sacoche_jointure_user_matiere '
          . 'LEFT JOIN sacoche_matiere USING (matiere_id) '
          . 'WHERE user_id=:user_id AND matiere_active=1 '
          . 'ORDER BY matiere_nom ASC';
  $DB_VAR = array( ':user_id' => $prof_id );
  return DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * lister_classes_eleves_from_groupes
 *
 * @param string   $listing_groupe_id   id des groupes séparés par des virgules
 * @return array
 */
public static function DB_lister_classes_eleves_from_groupes($listing_groupe_id)
{
  $DB_SQL = 'SELECT groupe_id, eleve_classe_id '
          . 'FROM sacoche_jointure_user_groupe '
          . 'LEFT JOIN sacoche_user USING (user_id) '
          . 'LEFT JOIN sacoche_user_profil USING (user_profil_sigle) '
          . 'WHERE groupe_id IN ('.$listing_groupe_id.') AND user_profil_type=:profil_type AND user_sortie_date>NOW() AND eleve_classe_id!=0 '
          . 'GROUP BY groupe_id, eleve_classe_id ';
  $DB_VAR = array( ':profil_type' => 'eleve' );
  return DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR , TRUE);
}

/**
 * lister_groupes_besoins
 * Il s’agit des groupes de besoins auxquels un prof est rattaché, propriétaire ou pas.
 *
 * @param int    $prof_id
 * @return array
 */
public static function DB_lister_groupes_besoins($prof_id)
{
  $DB_SQL = 'SELECT jointure_pp, groupe_id, groupe_nom, niveau_id, niveau_ordre, niveau_nom '
          . 'FROM sacoche_jointure_user_groupe '
          . 'LEFT JOIN sacoche_groupe USING (groupe_id) '
          . 'LEFT JOIN sacoche_niveau USING (niveau_id) '
          . 'WHERE user_id=:user_id AND groupe_type=:type '
          . 'ORDER BY niveau_ordre ASC, groupe_nom ASC';
  $DB_VAR = array(
    ':user_id' => $prof_id,
    ':type'    => 'besoin',
  );
  return DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * lister_groupes_besoins_non_proprietaire_avec_infos
 * Il s’agit des groupes de besoins dont un prof n’est pas propriétaire, mais qui lui est affecté, avec l’info sur son propriétaire.
 *
 * @param int    $prof_id
 * @return array
 */
/*
public static function DB_lister_groupes_besoins_non_proprietaire_avec_infos($prof_id)
{
  $DB_SQL = 'SELECT groupe_id, groupe_nom, user_nom, user_prenom '
          . 'FROM sacoche_user AS locataire '
          . 'LEFT JOIN sacoche_jointure_user_groupe ON locataire.user_id=sacoche_jointure_user_groupe.user_id '
          . 'LEFT JOIN sacoche_groupe USING (groupe_id) '
          . 'LEFT JOIN sacoche_user AS locataire '
          . 'WHERE user_id=:user_id AND jointure_pp=:proprio AND groupe_type=:type '
          . 'ORDER BY niveau_ordre ASC, groupe_nom ASC';
  $DB_VAR = array(
    ':user_id' => $prof_id,
    ':proprio' => 1,
    ':type'    => 'besoin',
  );
  return DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}
*/

/**
 * lister_eleves_classes
 *
 * @param string   $listing_classe_id   id des classes séparés par des virgules
 * @return array
 */
public static function DB_lister_eleves_classes($listing_classe_id)
{
  $DB_SQL = 'SELECT user_id, user_nom, user_prenom, user_login, eleve_classe_id '
          . 'FROM sacoche_user '
          . 'LEFT JOIN sacoche_user_profil USING (user_profil_sigle) '
          . 'WHERE user_profil_type=:profil_type AND user_sortie_date>NOW() AND eleve_classe_id IN ('.$listing_classe_id.') '
          . 'ORDER BY user_nom ASC, user_prenom ASC';
  $DB_VAR = array( ':profil_type' => 'eleve' );
  return DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * lister_eleves_groupes
 *
 * @param string   $listing_groupe_id   id des groupes séparés par des virgules
 * @return array
 */
public static function DB_lister_eleves_groupes($listing_groupe_id)
{
  $DB_SQL = 'SELECT user_id, user_nom, user_prenom, user_login, groupe_id '
          . 'FROM sacoche_jointure_user_groupe '
          . 'LEFT JOIN sacoche_user USING (user_id) '
          . 'LEFT JOIN sacoche_user_profil USING (user_profil_sigle) '
          . 'WHERE user_profil_type=:profil_type AND user_sortie_date>NOW() AND groupe_id IN ('.$listing_groupe_id.') '
          . 'ORDER BY user_nom ASC, user_prenom ASC';
  $DB_VAR = array( ':profil_type' => 'eleve' );
  return DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * lister_users_avec_groupes_besoins
 *
 * @param string   $listing_groupes_id   liste des ids des groupes séparés par des virgules
 * @return array
 */
public static function DB_lister_users_avec_groupes_besoins($listing_groupes_id)
{
  $DB_SQL = 'SELECT user_id, user_nom, user_prenom, user_profil_type, groupe_id, jointure_pp '
          . 'FROM sacoche_jointure_user_groupe '
          . 'LEFT JOIN sacoche_user USING (user_id) '
          . 'LEFT JOIN sacoche_user_profil USING (user_profil_sigle) '
          . 'WHERE groupe_id IN('.$listing_groupes_id.') AND user_sortie_date>NOW() '
          . 'ORDER BY user_nom ASC, user_prenom ASC';
  return DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL);
}

/**
 * Retourner un tableau [valeur texte optgroup] des élèves d’un professeur identifié
 *
 * @param int    $prof_id
 * @param string $user_join_groupes
 * @return array|string
 */
public static function DB_OPT_lister_eleves_professeur( $prof_id , $user_join_groupes )
{
  // sous-requêtes [http://dev.mysql.com/doc/refman/5.0/fr/subqueries.html]
  if($user_join_groupes=='config')
  {
    $requete_id_classes = 'SELECT groupe_id FROM sacoche_jointure_user_groupe LEFT JOIN sacoche_groupe USING (groupe_id) WHERE user_id=:user_id AND groupe_type=:type1';
    $requete_id_groupes = 'SELECT groupe_id FROM sacoche_jointure_user_groupe LEFT JOIN sacoche_groupe USING (groupe_id) WHERE user_id=:user_id AND groupe_type=:type2';
  }
  else
  {
    $requete_id_classes = 'SELECT groupe_id FROM sacoche_groupe WHERE groupe_type=:type1';
    $requete_id_groupes = 'SELECT groupe_id FROM sacoche_groupe WHERE groupe_type=:type2';
  }
  // éléments des deux requêtes
  $sql_select = 'SELECT sacoche_user.user_id AS valeur, CONCAT(user_nom," ",user_prenom) AS texte, groupe_nom AS optgroup , groupe_type, niveau_ordre ';
  $sql_from   = 'FROM sacoche_groupe LEFT JOIN sacoche_niveau USING (niveau_id) ';
  $sql_where  = 'WHERE user_profil_type=:profil_type AND user_sortie_date>NOW() ';
  $sql_join_classe = 'LEFT JOIN sacoche_user ON sacoche_groupe.groupe_id = sacoche_user.eleve_classe_id ';
  $sql_join_groupe = 'LEFT JOIN sacoche_jointure_user_groupe USING (groupe_id) LEFT JOIN sacoche_user USING(user_id) ';
  $sql_join_profil = 'LEFT JOIN sacoche_user_profil USING (user_profil_sigle) ';
  // les deux requêtes
  $DB_SQL_CLASSE = $sql_select.$sql_from.$sql_join_classe.$sql_join_profil.$sql_where.'AND groupe_id IN ('.$requete_id_classes.')';
  $DB_SQL_GROUPE = $sql_select.$sql_from.$sql_join_groupe.$sql_join_profil.$sql_where.'AND groupe_id IN ('.$requete_id_groupes.')';
  // Union des deux requêtes [http://dev.mysql.com/doc/refman/5.0/fr/union.html]
  $DB_SQL = '( '.$DB_SQL_CLASSE.' ) UNION ( '.$DB_SQL_GROUPE.' ) ORDER BY niveau_ordre ASC, groupe_type ASC, optgroup ASC, texte ASC ';
  $DB_VAR = array(
    ':user_id'     => $prof_id,
    ':profil_type' => 'eleve',
    ':type1'       => 'classe',
    ':type2'       => 'groupe',
  );
  $DB_TAB = DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
  return !empty($DB_TAB) ? $DB_TAB : 'Aucun élève ne vous est affecté.' ;
}

/**
 * Retourner le listing des ids des élèves d’un professeur identifié
 *
 * @param int    $prof_id
 * @param string $user_join_groupes
 * @param string $format_retour   'string'|'array'
 * @return string | array
 */
public static function DB_lister_ids_eleves_professeur( $prof_id , $user_join_groupes , $format_retour )
{
  // sous-requêtes [http://dev.mysql.com/doc/refman/5.0/fr/subqueries.html]
  if($user_join_groupes=='config')
  {
    $requete_id_classes = 'SELECT groupe_id FROM sacoche_jointure_user_groupe LEFT JOIN sacoche_groupe USING (groupe_id) WHERE user_id=:user_id AND groupe_type=:type1';
    $requete_id_groupes = 'SELECT groupe_id FROM sacoche_jointure_user_groupe LEFT JOIN sacoche_groupe USING (groupe_id) WHERE user_id=:user_id AND groupe_type=:type2';
  }
  else
  {
    $requete_id_classes = 'SELECT groupe_id FROM sacoche_groupe WHERE groupe_type=:type1';
    $requete_id_groupes = 'SELECT groupe_id FROM sacoche_groupe WHERE groupe_type=:type2';
  }
  // Lever si besoin une limitation de GROUP_CONCAT (group_concat_max_len est par défaut limité à une chaîne de 1024 caractères) ; éviter plus de 8096 (http://www.glpi-project.org/forum/viewtopic.php?id=23767).
  DB::query(SACOCHE_STRUCTURE_BD_NAME , 'SET group_concat_max_len = 8096');
  // éléments des deux requêtes
  $sql_select = 'SELECT GROUP_CONCAT(DISTINCT sacoche_user.user_id SEPARATOR ",") AS listing_eleves_id ';
  $sql_from   = 'FROM sacoche_groupe LEFT JOIN sacoche_niveau USING (niveau_id) ';
  $sql_where  = 'WHERE user_profil_type=:profil_type AND user_sortie_date>NOW() ';
  $sql_join_classe = 'LEFT JOIN sacoche_user ON sacoche_groupe.groupe_id = sacoche_user.eleve_classe_id ';
  $sql_join_groupe = 'LEFT JOIN sacoche_jointure_user_groupe USING (groupe_id) LEFT JOIN sacoche_user USING(user_id) ';
  $sql_join_profil = 'LEFT JOIN sacoche_user_profil USING (user_profil_sigle) ';
  // les deux requêtes
  $DB_SQL_CLASSE = $sql_select.$sql_from.$sql_join_classe.$sql_join_profil.$sql_where.'AND groupe_id IN ('.$requete_id_classes.')';
  $DB_SQL_GROUPE = $sql_select.$sql_from.$sql_join_groupe.$sql_join_profil.$sql_where.'AND groupe_id IN ('.$requete_id_groupes.')';
  // Union des deux requêtes [http://dev.mysql.com/doc/refman/5.0/fr/union.html]
  $DB_SQL = '( '.$DB_SQL_GROUPE.' ) UNION ( '.$DB_SQL_CLASSE.' )';
  $DB_VAR = array(
    ':user_id'     => $prof_id,
    ':profil_type' => 'eleve',
    ':type1'       => 'classe',
    ':type2'       => 'groupe',
  );
  $DB_TAB = DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
  $tab_listing_id = array();
  foreach($DB_TAB as $DB_ROW)
  {
    if($DB_ROW['listing_eleves_id'] !== NULL)
    {
      $tab_listing_id[] = $DB_ROW['listing_eleves_id'];
    }
  }
  $listing_eleves_id = implode(',',$tab_listing_id);
  if($format_retour=='string')
  {
    return $listing_eleves_id;
  }
  else
  {
    return explode( ',' , $listing_eleves_id);
  }
}

/**
 * Retourner le listing des ids des collègues associés à un professeur pour un remplacement
 *
 * @param int   $prof_id
 * @return array
 */
public static function DB_lister_ids_remplacement( $prof_id )
{
  $DB_SQL = 'SELECT prof_absent_id, prof_remplacant_id '
          . 'FROM sacoche_jointure_prof_remplacement '
          . 'LEFT JOIN sacoche_user AS absent ON sacoche_jointure_prof_remplacement.prof_absent_id=absent.user_id '
          . 'LEFT JOIN sacoche_user AS remplacant ON sacoche_jointure_prof_remplacement.prof_remplacant_id=remplacant.user_id '
          . 'WHERE ( prof_absent_id=:prof_absent_id OR prof_remplacant_id=:prof_remplacant_id ) '
          . 'AND absent.user_sortie_date>NOW() AND remplacant.user_sortie_date>NOW() ';
  $DB_VAR = array(
    ':prof_absent_id'     => $prof_id,
    ':prof_remplacant_id' => $prof_id,
  );
  $DB_TAB = DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
  $tab_id_remplacement = array();
  if(!empty($DB_TAB))
  {
    foreach($DB_TAB as $DB_ROW)
    {
      $key = ( $DB_ROW['prof_absent_id'] == $prof_id ) ? 'prof_remplacant_id' : 'prof_absent_id' ;
      $tab_id_remplacement[$DB_ROW[$key]] = 'voir';
    }
  }
  return $tab_id_remplacement;
}

/**
 * Retourner un tableau [valeur] des id d'items pour comparaison avec ceux transmis
 *
 * @param string $listing_item_id
 * @return array
 */
public static function DB_lister_id_items( $listing_item_id )
{
  $DB_SQL = 'SELECT item_id '
          . 'FROM sacoche_referentiel_item '
          . 'WHERE item_id IN ('.$listing_item_id.') ';
  return DB::queryCol(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL);
}

/**
 * Lister les élèves ayant déjà fait une évaluation de nom donné avec un prof donné à partir d’une date donnée.
 * Seules les évaluations sur des sélections d’élèves sont prises en compte.
 * MAX() pour éviter l’erreur "SELECT list is not in GROUP BY clause and contains nonaggregated column 'sacoche_devoir.devoir_date' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by"
 *
 * @param int    $prof_id
 * @param string $devoir_info
 * @param string $date_debut_sql
 * @return array
 */
public static function DB_lister_eleves_devoirs( $prof_id , $devoir_info , $date_debut_sql )
{
  $position_description = mb_strpos( $devoir_info , '#' );
  if($position_description)
  {
    $devoir_info = mb_substr( $devoir_info , 0 , $position_description );
  }
  $DB_SQL = 'SELECT user_id, MAX(devoir_date) AS devoir_date '
          . 'FROM sacoche_devoir '
          . 'LEFT JOIN sacoche_groupe USING (groupe_id) '
          . 'LEFT JOIN sacoche_jointure_user_groupe USING (groupe_id) '
          . 'LEFT JOIN sacoche_jointure_devoir_prof USING (devoir_id) '
          . 'WHERE ( sacoche_devoir.proprio_id=:proprio_id OR sacoche_jointure_devoir_prof.prof_id=:prof_id ) '
          . 'AND devoir_info LIKE :devoir_info '
          . 'AND devoir_date>=:date_debut '
          . 'AND groupe_type=:type4 '
          . 'GROUP BY user_id ';
  $DB_VAR = array(
    ':proprio_id'  => $prof_id,
    ':prof_id'     => $prof_id,
    ':devoir_info' => $devoir_info.'%',
    ':date_debut' => $date_debut_sql,
    ':type4'       => 'eval',
  );
  return DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * lister_devoirs_prof
 *
 * @param int    $prof_id
 * @param int    $groupe_id        id du groupe ou de la classe pour un devoir sur une classe ou un groupe ; 0 pour un devoir sur une sélection d’élèves ; -1 pour les devoirs de toutes les classes / tous les groupes
 * @param string $date_sql_debut
 * @param string $date_sql_fin
 * @param bool   $eval_restriction_fini
 * @param bool   $eval_restriction_proprio
 * @return array
 */
public static function DB_lister_devoirs_prof( $prof_id , $groupe_id , $date_sql_debut , $date_sql_fin , $eval_restriction_fini , $eval_restriction_proprio )
{
  // Lever si besoin une limitation de GROUP_CONCAT (group_concat_max_len est par défaut limité à une chaîne de 1024 caractères) ; éviter plus de 8096 (http://www.glpi-project.org/forum/viewtopic.php?id=23767).
  DB::query(SACOCHE_STRUCTURE_BD_NAME , 'SET group_concat_max_len = 8096');
  // Il faut commencer par lister les ids de devoirs sinon en cas de jointure du prof sur sacoche_jointure_devoir_prof on ne récupère pas la liste des autres profs associés à l’éval.
  $where_devoir_proprio = ($eval_restriction_proprio) ? 'sacoche_devoir.proprio_id=:proprio_id ' : '( sacoche_devoir.proprio_id=:proprio_id OR sacoche_jointure_devoir_prof.prof_id=:prof_id ) ' ;
  $where_devoir_fini    = ($eval_restriction_fini) ? 'AND devoir_fini=0 ' : '' ;
  $DB_SQL = 'SELECT GROUP_CONCAT(DISTINCT devoir_id SEPARATOR ",") AS devoirs_listing '
          . 'FROM sacoche_devoir '
          . 'LEFT JOIN sacoche_jointure_devoir_prof USING (devoir_id) '
          . 'WHERE '.$where_devoir_proprio.$where_devoir_fini;
  $DB_VAR = array(
    ':proprio_id' => $prof_id,
    ':prof_id'    => $prof_id,
  );
  $devoirs_listing = DB::queryOne(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
  if(empty($devoirs_listing))
  {
    return array();
  }
  // Il faut ajouter dans la requête des "DISTINCT" sinon la liaison avec "sacoche_jointure_user_groupe" duplique tout x le nb d’élèves associés pour une évaluation sur une sélection d’élèves.
  // De même pour compter les sujets / corrigés / copies, SUM( IF( ... IS NOT NULL , 1 , 0 ) ) ne fonctionne pas car le résultat est multiplié par le nombre d’items, on utilise donc un COUNT(DISTINCT ...) classique (qui ne compte pas les valeurs NULL, ce qui tombe bien).
  $select_users_listing = ($groupe_id) ? '' : 'GROUP_CONCAT(DISTINCT sacoche_jointure_user_groupe.user_id SEPARATOR "_") AS users_listing, ' ;
  $select_users_nombre  = ($groupe_id) ? '' : 'COUNT(DISTINCT sacoche_jointure_user_groupe.user_id) AS users_nombre, ';
  $join_user_groupe     = ($groupe_id) ? '' : 'LEFT JOIN sacoche_jointure_user_groupe USING (groupe_id) ' ;
  $join_user            = ($groupe_id) ? '' : 'LEFT JOIN sacoche_user AS eleves ON sacoche_jointure_user_groupe.user_id=eleves.user_id ';
  $where_groupe_type = ($groupe_id!=0) ? 'AND groupe_type!=:type4 ' : 'AND groupe_type=:type4 ' ;
  $where_groupe_id   = ($groupe_id>0)  ? 'AND groupe_id='.$groupe_id.' ' : '' ;
  $where_profil      = ($groupe_id==0) ? 'AND eleves.user_profil_sigle="ELV" ' : '' ; // Sinon les prof (aussi rattachés au groupe du devoir) sont comptés parmi la liste des élèves.
  $DB_SQL = 'SELECT sacoche_devoir.*, CONCAT(prof.user_nom," ",prof.user_prenom) AS proprietaire, '
          . 'GROUP_CONCAT(DISTINCT sacoche_jointure_devoir_item.item_id SEPARATOR "_") AS items_listing, '
          . 'COUNT(DISTINCT sacoche_jointure_devoir_item.item_id) AS items_nombre, '
          . 'COUNT(DISTINCT jointure_doc_sujet) as sujets_nombre, '
          . 'COUNT(DISTINCT jointure_doc_corrige) as corriges_nombre, '
          . 'COUNT(DISTINCT jointure_doc_copie) as copies_nombre, '
          . 'GROUP_CONCAT(DISTINCT CONCAT(SUBSTRING(sacoche_jointure_devoir_prof.jointure_droit,1,1),sacoche_jointure_devoir_prof.prof_id) SEPARATOR "_") AS partage_listing, '
          . $select_users_listing.$select_users_nombre.'groupe_type, groupe_nom '
          . 'FROM sacoche_devoir '
          . 'LEFT JOIN sacoche_jointure_devoir_item USING (devoir_id) '
          . 'LEFT JOIN sacoche_jointure_devoir_prof USING (devoir_id) '
          . 'LEFT JOIN sacoche_jointure_devoir_eleve USING (devoir_id) '
          . 'LEFT JOIN sacoche_groupe USING (groupe_id) '
          . $join_user_groupe
          . $join_user
          . 'LEFT JOIN sacoche_user AS prof ON sacoche_devoir.proprio_id=prof.user_id '
          . 'WHERE devoir_id IN ('.$devoirs_listing.') '.$where_groupe_type.$where_groupe_id.$where_profil
          . 'AND devoir_date >= :date_debut AND devoir_date <= :date_fin '
          . 'GROUP BY sacoche_devoir.devoir_id '
          . 'ORDER BY devoir_date DESC, groupe_nom ASC';
  $DB_VAR = array(
    ':date_debut' => $date_sql_debut,
    ':date_fin'   => $date_sql_fin,
    ':type4'      => 'eval',
  );
  return DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * lister_devoirs_prof_groupe_sans_infos_last
 * Pour l’enseignant propriétaire du devoir et les enseignants ayant un accès en modification.
 *
 * @param int    $prof_id
 * @param int    $groupe_id
 * @param string $groupe_type   groupe | select
 * @return array
 */
public static function DB_lister_devoirs_prof_groupe_sans_infos_last( $prof_id , $groupe_id , $groupe_type )
{
  $where_groupe = ($groupe_type=='groupe') ? 'AND groupe_type!=:type4 AND groupe_id=:groupe_id ' : 'AND groupe_type=:type4 ' ;
  $DB_SQL = 'SELECT devoir_id, groupe_id, devoir_info, devoir_date, devoir_visible_date, devoir_saisie_visible_date '
          . 'FROM sacoche_devoir '
          . 'LEFT JOIN sacoche_jointure_devoir_prof USING (devoir_id) '
          . 'LEFT JOIN sacoche_groupe USING (groupe_id) '
          . 'WHERE ( sacoche_devoir.proprio_id=:proprio_id OR ( sacoche_jointure_devoir_prof.prof_id=:prof_id AND jointure_droit=:jointure_droit ) ) '
          . $where_groupe.'AND ( devoir_diagnostic = 0 OR devoir_diagnostic IS NULL ) ' // Pas d’évaluations diagnostiques ici
          . 'GROUP BY devoir_id '
          . 'ORDER BY devoir_date DESC '
          . 'LIMIT 20 ';
  $DB_VAR = array(
    ':proprio_id'     => $prof_id,
    ':prof_id'        => $prof_id,
    ':groupe_id'      => $groupe_id,
    ':jointure_droit' => 'modifier',
    ':type4'          => 'eval',
  );
  return DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * lister_devoirs_prof_groupe_sans_infos_last
 * Pour l’enseignant propriétaire du devoir et les enseignants ayant un accès en modification.
 *
 * @param int    $prof_id
 * @param int    $groupe_id
 * @param string $groupe_type   classe | groupe
 * @param string $date_debut_sql
 * @param string $date_fin_sql
 * @param string $diagnostic    oui | non
 * @return array
 */
public static function DB_OPT_eleves_devoirs_prof_groupe( $prof_id , $groupe_id , $groupe_type , $date_debut_sql , $date_fin_sql , $diagnostic )
{
  // Lever si besoin une limitation de GROUP_CONCAT (group_concat_max_len est par défaut limité à une chaîne de 1024 caractères) ; éviter plus de 8096 (http://www.glpi-project.org/forum/viewtopic.php?id=23767).
  DB::query(SACOCHE_STRUCTURE_BD_NAME , 'SET group_concat_max_len = 8096');
  // Liste des élèves de ce groupe
  $DB_VAR = array( ':groupe_id' => $groupe_id , ':profil_sigle' => 'ELV' );
  if($groupe_type=='classe')
  {
    $DB_SQL = 'SELECT GROUP_CONCAT(DISTINCT user_id SEPARATOR ",") AS listing_eleve_id '
            . 'FROM sacoche_user '
            . 'WHERE eleve_classe_id=:groupe_id';
  }
  else
  {
    $DB_SQL = 'SELECT GROUP_CONCAT(DISTINCT user_id SEPARATOR ",") AS listing_eleve_id '
            . 'FROM sacoche_jointure_user_groupe '
            . 'LEFT JOIN sacoche_user USING (user_id) '
            . 'WHERE groupe_id=:groupe_id AND user_profil_sigle=:profil_sigle ';
  }
  $listing_eleve_id = DB::queryOne(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
  if(empty($listing_eleve_id))
  {
    return 'Aucun élève trouvé dans ce regroupement.' ;
  }
  // Liste des classes / groupes avec au moins un de ces élèves
  $DB_SQL = 'SELECT GROUP_CONCAT(DISTINCT groupe_id SEPARATOR ",") AS listing_groupe_id FROM sacoche_jointure_user_groupe WHERE user_id IN('.$listing_eleve_id.')';
  $listing_groupe_id = DB::queryOne(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL);
  if($groupe_type=='classe')
  {
    $listing_classe_id = $groupe_id;
  }
  else
  {
    $DB_SQL = 'SELECT GROUP_CONCAT(DISTINCT eleve_classe_id SEPARATOR ",") AS listing_classe_id FROM sacoche_user WHERE user_id IN('.$listing_eleve_id.') AND eleve_classe_id != 0 ';
    $listing_classe_id = DB::queryOne(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL);
  }
  if( !empty($listing_classe_id) && !empty($listing_groupe_id) )
  {
    $listing_classe_groupe = $listing_classe_id.','.$listing_groupe_id;
  }
  else
  {
    $listing_classe_groupe = empty($listing_groupe_id) ? $listing_classe_id : $listing_groupe_id;
  }
  $diagnostic_value = ($diagnostic=='oui') ? 1 : 0 ;
  $DB_SQL = 'SELECT devoir_id AS valeur, devoir_info AS texte , devoir_date , groupe_nom '
          . 'FROM sacoche_devoir '
          . 'LEFT JOIN sacoche_jointure_devoir_prof USING (devoir_id) '
          . 'LEFT JOIN sacoche_groupe USING (groupe_id) '
          . 'WHERE ( sacoche_devoir.proprio_id=:proprio_id OR sacoche_jointure_devoir_prof.prof_id=:prof_id ) '
          . 'AND groupe_id IN ('.$listing_classe_groupe.') '
          . 'AND devoir_date >= :date_debut AND devoir_date <= :date_fin '
          . 'AND devoir_diagnostic = :diagnostic '
          . 'GROUP BY devoir_id '
          . 'ORDER BY devoir_date DESC ';
  $DB_VAR = array(
    ':proprio_id'     => $prof_id,
    ':prof_id'        => $prof_id,
    ':jointure_droit' => 'modifier',
    ':date_debut'     => $date_debut_sql,
    ':date_fin'       => $date_fin_sql,
    ':diagnostic'     => $diagnostic_value,
  );
  $DB_TAB = DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
  $diagnostic_texte = ($diagnostic=='oui') ? ' diagnostique' : '' ;
  return !empty($DB_TAB) ? $DB_TAB : 'Aucune évaluation'.$diagnostic_texte.' trouvée pour ce regroupement sur cette période.' ;
}

/**
 * lister_devoir_items
 * Retourner les items d’un devoir
 *
 * @param int   $devoir_id
 * @param bool  $with_socle
 * @param bool  $with_coef
 * @param bool  $with_ref
 * @param bool  $with_lien
 * @param bool  $with_module
 * @param bool  $with_comm
 * @param bool  $with_domaine
 * @param bool  $with_theme
 * @return array
 */
public static function DB_lister_devoir_items( $devoir_id , $with_socle , $with_coef , $with_ref , $with_lien , $with_module , $with_comm , $with_domaine , $with_theme )
{
  $select_count_socle = ($with_socle)   ? 'COUNT(sacoche_jointure_referentiel_socle.item_id) AS s2016_nb, ' : '' ;
  $select_coef        = ($with_coef)    ? 'item_coef, '   : '' ;
  $select_lien        = ($with_lien)    ? 'item_lien, '   : '' ;
  $select_mat_ref     = ($with_ref)     ? 'matiere_ref, ' : '' ;
  $select_ref_auto    = ($with_ref)     ? 'CONCAT(niveau_ref,".",domaine_code,theme_ordre,item_ordre) AS ref_auto, ' : '' ;
  $select_ref_perso   = ($with_ref)     ? 'CONCAT(domaine_ref,theme_ref,item_ref) AS ref_perso, ' : '' ;
  $select_module        = ($with_module)  ? 'item_module, ' : '' ;
  $select_comm        = ($with_comm)    ? 'item_comm, '   : '' ;
  $select_domaine     = ($with_domaine) ? 'domaine_nom, ' : '' ;
  $select_theme       = ($with_theme)   ? 'theme_nom, '   : '' ;
  $join_socle         = ($with_socle)   ? 'LEFT JOIN sacoche_jointure_referentiel_socle USING (item_id) ' : '' ;
  $groupby_item       = ($with_socle)   ? 'GROUP BY sacoche_referentiel_item.item_id ' : '' ;
  // Depuis MySQL 5.7.5 la directive ONLY_FULL_GROUP_BY est activée ce qui plantait la requête ci-dessous
  // (SELECT list is not in GROUP BY clause and contains nonaggregated column 'sacoche_jointure_devoir_item.item_id' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by)
  // d’où le remplacement d’un LEFT JOIN par un INNER JOIN
  $DB_SQL = 'SELECT item_id, '.$select_count_socle.$select_coef.$select_lien.$select_mat_ref.$select_ref_auto.$select_ref_perso.$select_module.$select_comm.$select_domaine.$select_theme.'item_nom '
          . 'FROM sacoche_jointure_devoir_item '
          . 'INNER JOIN sacoche_referentiel_item USING (item_id) '
          . 'LEFT JOIN sacoche_referentiel_theme USING (theme_id) '
          . 'LEFT JOIN sacoche_referentiel_domaine USING (domaine_id) '
          . 'LEFT JOIN sacoche_niveau USING (niveau_id) '
          . 'LEFT JOIN sacoche_matiere USING (matiere_id) '
          . $join_socle
          . 'WHERE devoir_id=:devoir_id '
          . $groupby_item
          . 'ORDER BY jointure_ordre ASC, matiere_ref ASC, niveau_ordre ASC, domaine_ordre ASC, theme_ordre ASC, item_ordre ASC';
  $DB_VAR = array( ':devoir_id' => $devoir_id );
  return DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * lister_devoir_saisies
 *
 * @param int   $devoir_id
 * @param bool  $with_marqueurs   // Avec ou sans les marqueurs de demandes d’évaluations
 * @return array
 */
public static function DB_lister_devoir_saisies( $devoir_id , $with_marqueurs )
{
  $where_marqueur = ($with_marqueurs) ? '' : 'AND saisie_note!="PA" ' ;
  // On évite les élèves désactivés pour ces opérations effectuées sur les pages de saisies d’évaluations
  $DB_SQL = 'SELECT eleve_id, item_id, saisie_note, prof_id '
          . 'FROM sacoche_saisie '
          . 'LEFT JOIN sacoche_user ON sacoche_saisie.eleve_id=sacoche_user.user_id '
          . 'WHERE devoir_id=:devoir_id AND user_sortie_date>NOW() '.$where_marqueur;
  $DB_VAR = array( ':devoir_id' => $devoir_id );
  return DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * lister_derniers_resultats_saisis
 *
 * @param int   $prof_id
 * @param int   $nb_jours
 * @return array
 */
public static function DB_lister_derniers_resultats_saisis( $prof_id , $nb_jours )
{
  $DB_SQL = 'SELECT eleve_id , item_id '
          . 'FROM sacoche_saisie '
          . 'INNER JOIN sacoche_devoir USING (devoir_id) ' // INNER JOIN pour les seules évals de cette année scolaire
          . 'LEFT JOIN sacoche_referentiel_item USING (item_id) '
          . 'LEFT JOIN sacoche_referentiel_theme USING (theme_id) '
          . 'LEFT JOIN sacoche_referentiel_domaine USING (domaine_id) '
          . 'LEFT JOIN sacoche_matiere USING (matiere_id) '
          . 'WHERE prof_id=:prof_id '
          . 'AND saisie_date > DATE_SUB( NOW() , INTERVAL :nb_jours DAY ) '
          . 'AND ( devoir_diagnostic = 0 OR devoir_diagnostic IS NULL ) ' // Pas d’évaluations diagnostiques ici
          . 'AND matiere_experimentale = 0 '; // Pas de matière expérimentale ici
  $DB_VAR = array(
    ':prof_id' => $prof_id,
    ':nb_jours' => $nb_jours,
  );
  return DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * Retourner les effectifs des classes / groupes
 *
 * @param int    $groupe_id   Pour restreindre à un groupe donné
 * @return array|int   tableau (groupe_id;eleves_nombre) si pas de restriction à un groupe donné, l’effectif du groupe concerné sinon
 */
public static function DB_lister_effectifs_groupes($groupe_id=0)
{
  // éléments des deux requêtes
  $sql_select = 'SELECT COUNT(DISTINCT sacoche_user.user_id) AS eleves_nombre ';
  $sql_select.= ($groupe_id) ? '' : ', groupe_id ';
  $sql_from   = 'FROM sacoche_groupe ';
  $sql_where  = 'WHERE user_profil_type=:profil_type AND user_entree_date<NOW() AND user_sortie_date>NOW() ';
  $sql_where .= ($groupe_id) ? 'AND groupe_id=:groupe_id ' : '' ;
  $sql_join_classe = 'LEFT JOIN sacoche_user ON sacoche_groupe.groupe_id = sacoche_user.eleve_classe_id ';
  $sql_join_groupe = 'LEFT JOIN sacoche_jointure_user_groupe USING (groupe_id) LEFT JOIN sacoche_user USING(user_id) ';
  $sql_join_profil = 'LEFT JOIN sacoche_user_profil USING (user_profil_sigle) ';
  $sql_group_by = 'GROUP BY groupe_id ';
  // les deux requêtes
  $DB_SQL_CLASSE = $sql_select.$sql_from.$sql_join_classe.$sql_join_profil.$sql_where.'AND groupe_type=:type1 '           .$sql_group_by;
  $DB_SQL_GROUPE = $sql_select.$sql_from.$sql_join_groupe.$sql_join_profil.$sql_where.'AND groupe_type IN(:type2,:type3) '.$sql_group_by;
  // Union des deux requêtes [http://dev.mysql.com/doc/refman/5.0/fr/union.html]
  $DB_SQL = '( '.$DB_SQL_CLASSE.' ) UNION ( '.$DB_SQL_GROUPE.' )';
  $DB_VAR = array(
    ':groupe_id'   => $groupe_id,
    ':profil_type' => 'eleve',
    ':type1'       => 'classe',
    ':type2'       => 'groupe',
    ':type3'       => 'besoin',
  );
  return ($groupe_id) ? (int)DB::queryOne(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR) : DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR) ;
}

/**
 * lister_nb_saisies_par_evaluation
 *
 * @param int|string   $listing_devoir_id   soit une chaine de devoir (string), soit un devoir unique (int)
 * @return array|int   tableau (devoir_id;saisies_nombre) si pas de restriction à un unique devoir, le nb de saisies du devoir concerné sinon
 */
public static function DB_lister_nb_saisies_par_evaluation($listing_devoir_id)
{
  $is_devoir_unique = is_int($listing_devoir_id) ? TRUE : FALSE ; 
  $select_devoir_id = ($is_devoir_unique) ? '' : ', devoir_id ';
  $DB_SQL = 'SELECT COUNT(saisie_note) AS saisies_nombre '.$select_devoir_id
          . 'FROM sacoche_saisie '
          . 'LEFT JOIN sacoche_user ON sacoche_saisie.eleve_id=sacoche_user.user_id '
          . 'WHERE devoir_id IN('.$listing_devoir_id.') ' 
          . 'AND saisie_note!="PA" AND user_entree_date<=saisie_date AND user_sortie_date>saisie_date ' 
          . 'GROUP BY devoir_id ';
  return ($is_devoir_unique) ? (int)DB::queryOne(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL) : DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL) ;
}

/**
 * lister_liaisons_user_module
 *
 * @param int $user_id
 * @return array
 */
public static function DB_lister_liaisons_user_module($user_id)
{
  $tab_module = array();
  $DB_SQL = 'SELECT module_objet , module_value '
          . 'FROM sacoche_jointure_user_module '
          . 'WHERE user_id=:user_id ' ;
  $DB_VAR = array( ':user_id' => $user_id );
  $DB_TAB = DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR) ;
  if(!empty($DB_TAB))
  {
    foreach($DB_TAB as $DB_ROW)
    {
      $tab_module[strtoupper($DB_ROW['module_objet'])] = $DB_ROW['module_value'];
    }
  }
  return $tab_module;
}

/**
 * tester_prof_principal
 *
 * @param int $prof_id
 * @param int $groupe_id   pour restreindre à une classe donnée
 * @return bool
 */
public static function DB_tester_prof_principal( $prof_id , $groupe_id )
{
  $where_groupe_id = ($groupe_id) ? 'AND groupe_id=:groupe_id ' : '' ;
  $DB_SQL = 'SELECT 1 '
          . 'FROM sacoche_jointure_user_groupe '
          . 'LEFT JOIN sacoche_groupe USING (groupe_id) '
          . 'WHERE user_id=:user_id AND jointure_pp=:pp AND groupe_type=:type '.$where_groupe_id
          . 'LIMIT 1 '; // utile
  $DB_VAR = array(
    ':user_id'   => $prof_id,
    ':pp'        => 1,
    ':type'      => 'classe',
    ':groupe_id' => $groupe_id,
  );
  return (bool)DB::queryOne(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * tester_prof_coordonnateur
 *
 * @param int $prof_id
 * @param int $matiere_id   pour restreindre à une matière donnée
 * @return bool
 */
public static function tester_prof_coordonnateur( $prof_id , $matiere_id )
{
  $where_matiere_id = ($matiere_id) ? 'AND matiere_id=:matiere_id ' : '' ;
  $DB_SQL = 'SELECT 1 '
          . 'FROM sacoche_jointure_user_matiere '
          . 'WHERE user_id=:user_id AND jointure_coord=:coord '.$where_matiere_id
          . 'LIMIT 1 '; // utile
  $DB_VAR = array(
    ':user_id'    => $prof_id,
    ':coord'      => 1,
    ':matiere_id' => $matiere_id,
  );
  return (bool)DB::queryOne(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * tester_groupe_nom
 *
 * @param string $groupe_nom
 * @param int    $groupe_id    inutile si recherche pour un ajout, mais id à éviter si recherche pour une modification
 * @return int
 */
public static function DB_tester_groupe_nom( $groupe_nom , $groupe_id=FALSE )
{
  $where_groupe_id = ($groupe_id) ? 'AND groupe_id!=:groupe_id ' : '' ;
  $DB_SQL = 'SELECT groupe_id '
          . 'FROM sacoche_groupe '
          . 'WHERE groupe_type=:groupe_type AND groupe_nom=:groupe_nom '.$where_groupe_id
          . 'LIMIT 1 '; // utile
  $DB_VAR = array(
    ':groupe_type' => 'besoin',
    ':groupe_nom'  => $groupe_nom,
    ':groupe_id'   => $groupe_id,
  );
  return (int)DB::queryOne(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * ajouter_devoir
 *
 * @param int        $prof_id
 * @param int        $groupe_id
 * @param string     $date_sql
 * @param string     $info
 * @param string     $date_devoir_visible_sql
 * @param string     $date_saisie_visible_sql
 * @param string     $date_autoeval_sql
 * @param string     $doc_sujet
 * @param string     $doc_corrige
 * @param int        $voir_repartition
 * @param int        $diagnostic
 * @param int        $pluriannuel
 * @param string|int $eleves_ordre   valeur parmi { "nom" ; "prenom" ; "classe" ; n° plan de classe }
 * @param int        $equipe
 * @return int
 */
public static function DB_ajouter_devoir( $prof_id , $groupe_id , $date_sql , $info , $date_devoir_visible_sql , $date_saisie_visible_sql , $date_autoeval_sql , $doc_sujet , $doc_corrige , $voir_repartition , $diagnostic , $pluriannuel , $eleves_ordre , $equipe )
{
  $DB_SQL = 'INSERT INTO sacoche_devoir( proprio_id, groupe_id, devoir_date, devoir_info, devoir_visible_date, devoir_saisie_visible_date, devoir_autoeval_date, devoir_doc_sujet, devoir_doc_corrige, devoir_voir_repartition, devoir_fini, devoir_diagnostic, devoir_pluriannuel, devoir_eleves_ordre, devoir_equipe) '
          . 'VALUES                    (:proprio_id,:groupe_id,:devoir_date,:devoir_info,:devoir_visible_date,:devoir_saisie_visible_date,:devoir_autoeval_date,:devoir_doc_sujet,:devoir_doc_corrige,:devoir_voir_repartition,:devoir_fini,:devoir_diagnostic,:devoir_pluriannuel,:devoir_eleves_ordre,:devoir_equipe)';
  $DB_VAR = array(
    ':proprio_id'                 => $prof_id,
    ':groupe_id'                  => $groupe_id,
    ':devoir_date'                => $date_sql,
    ':devoir_info'                => $info,
    ':devoir_visible_date'        => $date_devoir_visible_sql,
    ':devoir_saisie_visible_date' => $date_saisie_visible_sql,
    ':devoir_autoeval_date'       => $date_autoeval_sql,
    ':devoir_doc_sujet'           => $doc_sujet,
    ':devoir_doc_corrige'         => $doc_corrige,
    ':devoir_voir_repartition'    => $voir_repartition,
    ':devoir_fini'                => 0,
    ':devoir_diagnostic'          => $diagnostic,
    ':devoir_pluriannuel'         => $pluriannuel,
    ':devoir_eleves_ordre'        => (string)$eleves_ordre,
    ':devoir_equipe'              => $equipe,
  );
  DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
  return DB::getLastOid(SACOCHE_STRUCTURE_BD_NAME);
}

/**
 * ajouter_saisie
 * Si la note est un marqueur d’évaluation ("PA"), on utilise un REPLACE au lieu d’un INSERT car une saisie peut déjà exister (si le prof ajoute les demandes à un devoir existant).
 *
 * @param int    $prof_id
 * @param int    $eleve_id
 * @param int    $devoir_id
 * @param int    $item_id
 * @param string $item_date_sql
 * @param string $item_note
 * @param string $item_info
 * @param string $item_date_visible_sql
 * @return void
 */
public static function DB_ajouter_saisie( $prof_id , $eleve_id , $devoir_id , $item_id , $item_date_sql , $item_note , $item_info , $item_date_visible_sql )
{
  $commande = ($item_note!='PA') ? 'INSERT' : 'REPLACE' ;
  $DB_SQL = $commande.' INTO sacoche_saisie '
          . 'VALUES(:prof_id, :eleve_id, :devoir_id, :item_id, :item_date, :item_note, :item_info, :item_date_visible)';
  $DB_VAR = array(
    ':prof_id'           => $prof_id,
    ':eleve_id'          => $eleve_id,
    ':devoir_id'         => $devoir_id,
    ':item_id'           => $item_id,
    ':item_date'         => $item_date_sql,
    ':item_note'         => $item_note,
    ':item_info'         => $item_info,
    ':item_date_visible' => $item_date_visible_sql,
  );
  DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * modifier_ordre_item
 *
 * @param int    $devoir_id
 * @param array  $tab_items   tableau des id des items
 * @return void
 */
public static function DB_modifier_ordre_item( $devoir_id , $tab_items )
{
  $DB_SQL = 'UPDATE sacoche_jointure_devoir_item '
          . 'SET jointure_ordre=:ordre '
          . 'WHERE devoir_id=:devoir_id AND item_id=:item_id ';
  $ordre = 1;
  foreach($tab_items as $item_id)
  {
    $DB_VAR = array(
      ':devoir_id' => $devoir_id,
      ':item_id'   => $item_id,
      ':ordre'     => $ordre,
    );
    DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
    $ordre++;
  }
}

/**
 * modifier_saisie
 *
 * @param int    $prof_id
 * @param int    $eleve_id
 * @param int    $devoir_id
 * @param int    $item_id
 * @param string $saisie_note
 * @param string $saisie_info
 * @return int   0 ou 1 si modifié
 */ 
public static function DB_modifier_saisie( $prof_id , $eleve_id , $devoir_id , $item_id , $saisie_note , $saisie_info )
{
  $DB_SQL = 'UPDATE sacoche_saisie '
          . 'SET prof_id=:prof_id, saisie_note=:saisie_note, saisie_info=:saisie_info '
          . 'WHERE eleve_id=:eleve_id AND devoir_id=:devoir_id AND item_id=:item_id ';
  $DB_VAR = array(
    ':eleve_id'    => $eleve_id,
    ':devoir_id'   => $devoir_id,
    ':item_id'     => $item_id,
    ':prof_id'     => $prof_id,
    ':saisie_note' => $saisie_note,
    ':saisie_info' => $saisie_info,
  );
  DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
  return DB::rowCount(SACOCHE_STRUCTURE_BD_NAME);
}

/**
 * modifier_devoir
 * sont traités à part devoir_doc_sujet / devoir_doc_corrige / devoir_fini : voir DB_modifier_devoir_document() et DB_modifier_devoir_fini()
 *
 * @param int        $devoir_id
 * @param int        $proprio_id
 * @param string     $date_sql
 * @param string     $devoir_info
 * @param string     $proprietaire_archive
 * @param string     $date_devoir_visible_sql
 * @param string     $date_saisie_visible_sql
 * @param string     $date_autoeval_sql
 * @param int        $voir_repartition
 * @param int        $diagnostic
 * @param int        $pluriannuel
 * @param string|int $eleves_ordre   valeur parmi { "nom" ; "prenom" ; "classe" ; n° plan de classe }
 * @param int        $equipe
 * @return void
 */
public static function DB_modifier_devoir( $devoir_id , $proprio_id , $date_sql , $devoir_info , $proprietaire_archive , $date_devoir_visible_sql , $date_saisie_visible_sql , $date_autoeval_sql , $voir_repartition , $diagnostic , $pluriannuel , $eleves_ordre , $equipe )
{
  // sacoche_devoir (maj)
  $DB_SQL = 'UPDATE sacoche_devoir '
          . 'SET devoir_date=:date, devoir_info=:devoir_info, devoir_visible_date=:devoir_visible_date, devoir_saisie_visible_date=:devoir_saisie_visible_date, devoir_autoeval_date=:autoeval_date, devoir_voir_repartition=:devoir_voir_repartition, devoir_diagnostic=:devoir_diagnostic, devoir_pluriannuel=:devoir_pluriannuel, devoir_eleves_ordre=:eleves_ordre, devoir_equipe=:equipe '
          . 'WHERE devoir_id=:devoir_id AND proprio_id=:proprio_id ';
  $DB_VAR = array(
    ':date'                       => $date_sql,
    ':devoir_info'                => $devoir_info,
    ':devoir_visible_date'        => $date_devoir_visible_sql,
    ':devoir_saisie_visible_date' => $date_saisie_visible_sql,
    ':autoeval_date'              => $date_autoeval_sql,
    ':devoir_voir_repartition'    => $voir_repartition,
    ':devoir_diagnostic'          => $diagnostic,
    ':devoir_pluriannuel'         => $pluriannuel,
    ':eleves_ordre'               => (string)$eleves_ordre,
    ':equipe'                     => $equipe,
    ':devoir_id'                  => $devoir_id,
    ':proprio_id'                 => $proprio_id,
  );
  DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
  // sacoche_saisie (maj)
  $saisie_info = $devoir_info.' ('.$proprietaire_archive.')';
  $date_saisie_visible_sql = ( TODAY_SQL < $date_saisie_visible_sql ) ? $date_saisie_visible_sql : NULL ;
  $DB_SQL = 'UPDATE sacoche_saisie '
          . 'SET saisie_date=:saisie_date, saisie_info=:saisie_info, saisie_visible_date=:saisie_visible_date '
          . 'WHERE devoir_id=:devoir_id ';
  $DB_VAR = array(
    ':devoir_id'           => $devoir_id,
    ':saisie_date'         => $date_sql,
    ':saisie_info'         => $saisie_info,
    ':saisie_visible_date' => $date_saisie_visible_sql,
  );
  DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * modifier_devoir_document
 *
 * @param int    $devoir_id
 * @param string $objet   'sujet' | 'corrige'
 * @param string $fichier_nom
 * @return void
 */
public static function DB_modifier_devoir_document( $devoir_id , $objet , $fichier_nom )
{
  $DB_SQL = 'UPDATE sacoche_devoir '
          . 'SET devoir_doc_'.$objet.'=:fichier_nom '
          . 'WHERE devoir_id=:devoir_id ';
  $DB_VAR = array(
    ':fichier_nom' => $fichier_nom,
    ':devoir_id'   => $devoir_id,
  );
  DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * modifier_devoir_fini
 *
 * @param int    $devoir_id
 * @param string $fini   oui | non
 * @return void
 */
public static function DB_modifier_devoir_fini( $devoir_id , $fini )
{
  $fini = ($fini=='oui') ? 1 : 0 ;
  $DB_SQL = 'UPDATE sacoche_devoir '
          . 'SET devoir_fini=:fini '
          . 'WHERE devoir_id=:devoir_id ';
  $DB_VAR = array(
    ':fini'      => $fini,
    ':devoir_id' => $devoir_id,
  );
  DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * modifier_liaison_devoir_item
 *
 * @param int    $devoir_id
 * @param array  $tab_items   tableau des id des items
 * @param string $mode        {creer;dupliquer} => insertion dans un nouveau devoir || {substituer} => maj avec delete / insert || {ajouter} => maj avec insert uniquement
 * @param int    $devoir_ordonne_id   Dans le cas d’une duplication, id du devoir dont il faut récupérer l’ordre des items.
 * @return void
 */
public static function DB_modifier_liaison_devoir_item( $devoir_id , $tab_items , $mode , $devoir_ordonne_id=0 )
{
  if( ($mode=='creer') || ($mode=='dupliquer') )
  {
    // Dans le cas d’une duplication, il faut aller rechercher l’ordre éventuel des items de l’évaluation d’origine pour ne pas le perdre
    $tab_ordre = array();
    if($devoir_ordonne_id)
    {
      $DB_SQL = 'SELECT item_id,jointure_ordre FROM sacoche_jointure_devoir_item '
              . 'WHERE devoir_id=:devoir_id AND jointure_ordre>0 ';
      $DB_VAR = array(':devoir_id'=>$devoir_ordonne_id);
      $DB_TAB = DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
      if(!empty($DB_TAB))
      {
        foreach($DB_TAB as $DB_ROW)
        {
          $tab_ordre[$DB_ROW['item_id']] = $DB_ROW['jointure_ordre'];
        }
      }
    }
    // Insertion des items
    $DB_SQL = 'INSERT INTO sacoche_jointure_devoir_item(devoir_id,item_id,jointure_ordre) '
            . 'VALUES(:devoir_id, :item_id, :ordre) ';
    foreach($tab_items as $item_id)
    {
      $ordre = isset($tab_ordre[$item_id]) ? $tab_ordre[$item_id] : 0 ;
      $DB_VAR = array(
        ':devoir_id' => $devoir_id,
        ':item_id'   => $item_id,
        ':ordre'     => $ordre,
      );
      DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
    }
  }
  else
  {
    // On ne peut pas faire un REPLACE car si un enregistrement est présent ça fait un DELETE+INSERT et du coup on perd l’info sur l’ordre des items.
    // Alors on récupère la liste des items déjà présents, et on étudie les différences pour faire des DELETE et INSERT sélectifs
    // -> on récupère les items actuels
    $DB_SQL = 'SELECT item_id  '
            . 'FROM sacoche_jointure_devoir_item '
            . 'WHERE devoir_id=:devoir_id ';
    $DB_VAR = array(':devoir_id'=>$devoir_id);
    $tab_old_items = DB::queryCol(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
    // -> on supprime si besoin les anciens items associés à ce devoir qui ne sont plus dans la liste transmise
    // -> on supprime si besoin les saisies des anciens items associés à ce devoir qui ne sont plus dans la liste transmise
    //   (par rapport aux saisies superflues concernant les items, voir DB_modifier_liaison_devoir_item() )
    if($mode=='substituer')
    {
      $tab_items_supprimer = array_diff($tab_old_items,$tab_items);
      if(count($tab_items_supprimer))
      {
        $chaine_item_id = implode(',',$tab_items_supprimer);
        $DB_SQL = 'DELETE FROM sacoche_jointure_devoir_item '
                . 'WHERE devoir_id=:devoir_id AND item_id IN('.$chaine_item_id.')';
        $DB_VAR = array(':devoir_id'=>$devoir_id);
        DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
        // sacoche_saisie (retirer superflu concernant les items ; concernant les élèves voir DB_modifier_liaison_devoir_eleve() )
        $DB_SQL = 'DELETE FROM sacoche_saisie '
                . 'WHERE devoir_id=:devoir_id AND item_id IN('.$chaine_item_id.')';
        $DB_VAR = array(':devoir_id'=>$devoir_id);
        DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
      }
    }
    // -> on ajoute les nouveaux items non anciennement présents
    $tab_items_ajouter = array_diff($tab_items,$tab_old_items);
    if(count($tab_items_ajouter))
    {
      foreach($tab_items_ajouter as $item_id)
      {
        $DB_SQL = 'INSERT INTO sacoche_jointure_devoir_item(devoir_id,item_id) '
                . 'VALUES(:devoir_id, :item_id) ';
        $DB_VAR = array(
          ':devoir_id' => $devoir_id,
          ':item_id'   => $item_id,
        );
        DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
      }
    }
  }
}

/**
 * modifier_liaison_devoir_prof
 *
 * @param int    $devoir_id
 * @param array  $tab_profs   tableau [id_prof->droit]
 * @param string $mode        {creer} => insertion dans un nouveau devoir || {substituer} => maj avec update / delete / insert
 * @return array   sert pour ensuite effectuer des mises à jour de notifications
 */
public static function DB_modifier_liaison_devoir_prof( $devoir_id , $tab_profs , $mode )
{
  $tab_retour = array();
  if($mode=='creer')
  {
    // Insertion des droits
    $DB_SQL = 'INSERT INTO sacoche_jointure_devoir_prof(devoir_id,prof_id,jointure_droit) '
            . 'VALUES(:devoir_id, :prof_id, :droit) ';
    foreach($tab_profs as $prof_id => $droit)
    {
      $DB_VAR = array(
        ':devoir_id' => $devoir_id,
        ':prof_id'   => $prof_id,
        ':droit'     => $droit,
      );
      DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
      $tab_retour[$prof_id] = 'insert';
    }
    return $tab_retour;
  }
  elseif($mode=='substituer')
  {
    // On récupère la liste des droits déjà présents, et on étudie les différences pour faire des REPLACE / DELETE / INSERT sélectifs
    // -> on récupère les droits actuels
    $DB_SQL = 'SELECT prof_id, jointure_droit '
            . 'FROM sacoche_jointure_devoir_prof '
            . 'WHERE devoir_id=:devoir_id ';
    $DB_VAR = array( ':devoir_id' => $devoir_id );
    $tab_old_droits = DB::queryTab(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR, TRUE, TRUE);
    // -> on parcourt $tab_profs pour comparer avec ce qui est enregistré
    foreach($tab_profs as $prof_id => $droit)
    {
      if(isset($tab_old_droits[$prof_id]))
      {
        if($tab_old_droits[$prof_id]['jointure_droit']!=$droit)
        {
          // -> modification de droit
          $DB_SQL = 'UPDATE sacoche_jointure_devoir_prof '
                  . 'SET jointure_droit=:droit '
                  . 'WHERE devoir_id=:devoir_id AND prof_id=:prof_id ';
          $DB_VAR = array(
            ':devoir_id' => $devoir_id,
            ':prof_id'   => $prof_id,
            ':droit'     => $droit,
          );
          DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
          $tab_retour[$prof_id] = 'update';
        }
        unset($tab_old_droits[$prof_id]);
      }
      else
      {
        // -> ajout de droit
        $DB_SQL = 'INSERT INTO sacoche_jointure_devoir_prof(devoir_id,prof_id,jointure_droit) '
                . 'VALUES(:devoir_id, :prof_id, :droit) ';
        $DB_VAR = array(
          ':devoir_id' => $devoir_id,
          ':prof_id'   => $prof_id,
          ':droit'     => $droit,
        );
        DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
        $tab_retour[$prof_id] = 'insert';
      }
    }
    // -> on observe $tab_old_droits pour rechercher ce qui reste
    if(count($tab_old_droits))
    {
      $chaine_prof_id = implode(',',array_keys($tab_old_droits));
      // -> suppression de droit
      $DB_SQL = 'DELETE FROM sacoche_jointure_devoir_prof '
              . 'WHERE devoir_id=:devoir_id AND prof_id IN('.$chaine_prof_id.')';
      $DB_VAR = array( ':devoir_id' => $devoir_id );
      DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
      foreach($tab_old_droits as $prof_id => $tab)
      {
        $tab_retour[$prof_id] = 'delete';
      }
    }
    return $tab_retour;
  }
}

/**
 * modifier_liaison_devoir_eleve
 * Uniquement pour les évaluations de type 'eval' ; voir DB_modifier_liaison_devoir_groupe() pour les autres
 *
 * @param int    $prof_id
 * @param int    $devoir_id
 * @param int    $groupe_id
 * @param array  $tab_eleves   tableau des id des élèves
 * @param string $mode         'creer' pour un insert dans un nouveau devoir || 'substituer' pour une maj delete / insert || 'ajouter' pour maj insert uniquement
 * @return void
 */
public static function DB_modifier_liaison_devoir_eleve( $prof_id , $devoir_id , $groupe_id , $tab_eleves , $mode )
{
  DB_STRUCTURE_REGROUPEMENT::DB_modifier_liaison_user_groupe_par_prof( $prof_id , $groupe_id , $tab_eleves , array() /*tab_profs*/ , $mode , $devoir_id );
}

/**
 * modifier_liaison_devoir_groupe
 * Uniquement pour les évaluations sur une classe ou un groupe (pas de type 'eval')
 *
 * @param int    $devoir_id
 * @param int    $groupe_id
 * @return void
 */
public static function DB_modifier_liaison_devoir_groupe( $devoir_id , $groupe_id )
{
  // -> on récupère l’id du groupe antérieurement associé au devoir
  $DB_SQL = 'SELECT groupe_id '
          . 'FROM sacoche_devoir '
          . 'WHERE devoir_id=:devoir_id ';
  $DB_VAR = array( ':devoir_id' => $devoir_id );
  if( $groupe_id != DB::queryOne(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR) )
  {
    // sacoche_devoir (maj)
    $DB_SQL = 'UPDATE sacoche_devoir '
            . 'SET groupe_id=:groupe_id '
            . 'WHERE devoir_id=:devoir_id ';
    $DB_VAR = array(
      ':devoir_id' => $devoir_id,
      ':groupe_id' => $groupe_id,
    );
    DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
    // sacoche_saisie : on ne s’embête pas à essayer de voir s’il y aurait intersection entre les deux groupes, on supprime les saisies du groupe antérieur...
    $DB_SQL = 'DELETE FROM sacoche_saisie '
            . 'WHERE devoir_id=:devoir_id ';
    $DB_VAR = array( ':devoir_id' => $devoir_id );
    DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
  }
}

/**
 * modifier_liaison_user_module
 * insert/update ou delete selon que $module_value soit renseigné ou pas
 *
 * @param int    $user_id
 * @param string $module_objet 'generer_enonce' | 'importer_evaluation' | 'api'
 * @param string $module_value
 * @return void
 */
public static function DB_modifier_liaison_user_module( $user_id , $module_objet , $module_value )
{
  if($module_value)
  {
    $DB_SQL = 'INSERT INTO sacoche_jointure_user_module( user_id, module_objet, module_value) '
            . 'VALUES                                  (:user_id,:module_objet,:module_value) '
            . 'ON DUPLICATE KEY UPDATE module_value=:module_value ';
  }
  else
  {
    $DB_SQL = 'DELETE FROM sacoche_jointure_user_module '
            . 'WHERE user_id=:user_id AND module_objet=:module_objet ';
  }
  $DB_VAR = array(
    ':user_id'      => $user_id,
    ':module_objet' => $module_objet,
    ':module_value' => $module_value,
  );
  DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * supprimer_devoir_et_saisies
 *
 * @param int   $devoir_id
 * @return void
 */
public static function DB_supprimer_devoir_et_saisies($devoir_id)
{
  // Il faut aussi supprimer les jointures du devoir avec les items, les profs, les élèves
  $DB_SQL = 'DELETE sacoche_devoir, sacoche_saisie, sacoche_jointure_devoir_item, sacoche_jointure_devoir_prof, sacoche_jointure_devoir_eleve '
          . 'FROM sacoche_devoir '
          . 'LEFT JOIN sacoche_saisie USING (devoir_id) '
          . 'LEFT JOIN sacoche_jointure_devoir_item  USING (devoir_id) '
          . 'LEFT JOIN sacoche_jointure_devoir_prof  USING (devoir_id) '
          . 'LEFT JOIN sacoche_jointure_devoir_eleve USING (devoir_id) '
          . 'WHERE devoir_id=:devoir_id ';
  $DB_VAR = array( ':devoir_id' => $devoir_id );
  DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * supprimer_devoir_pluriannuel_et_archivage_saisies
 *
 * @param int   $devoir_id
 * @return void
 */
public static function DB_supprimer_devoir_pluriannuel_et_archivage_saisies($devoir_id)
{
  $DB_VAR = array( ':devoir_id' => $devoir_id );
  // Modifier les dates des saisies
  $DB_SQL = 'UPDATE sacoche_saisie '
          . 'SET saisie_date = DATE_SUB(saisie_date, INTERVAL 1 YEAR), '
          .   'saisie_visible_date = DATE_SUB(saisie_visible_date, INTERVAL 1 YEAR) ' // si NULL alors reste NULL
          . 'WHERE devoir_id=:devoir_id ';
  DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
  // Supprimer le devoir sans les saisies (comme dans le cadre d’un nettoyage annuel)
  // Supprimer le regroupement (forcément de type 'eval')
  $DB_SQL = 'DELETE sacoche_devoir , sacoche_groupe , sacoche_jointure_devoir_item , sacoche_jointure_devoir_prof , sacoche_jointure_devoir_eleve , sacoche_jointure_user_groupe '
          . 'FROM sacoche_devoir '
          . 'LEFT JOIN sacoche_jointure_devoir_item USING (devoir_id) '
          . 'LEFT JOIN sacoche_jointure_devoir_prof USING (devoir_id) '
          . 'LEFT JOIN sacoche_jointure_devoir_eleve USING (devoir_id) '
          . 'LEFT JOIN sacoche_groupe USING (groupe_id) '
          . 'LEFT JOIN sacoche_jointure_user_groupe USING (groupe_id) '
          . 'WHERE devoir_id=:devoir_id ';
  DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * supprimer_saisie
 *
 * @param int   $eleve_id
 * @param int   $devoir_id
 * @param int   $item_id
 * @return void
 */
public static function DB_supprimer_saisie( $eleve_id , $devoir_id , $item_id )
{
  $DB_SQL = 'DELETE FROM sacoche_saisie '
          . 'WHERE eleve_id=:eleve_id AND devoir_id=:devoir_id AND item_id=:item_id ';
  $DB_VAR = array(
    ':eleve_id'  => $eleve_id,
    ':devoir_id' => $devoir_id,
    ':item_id'   => $item_id,
  );
  DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

/**
 * supprimer_liaison_devoir_prof
 *
 * @param int   $devoir_id
 * @return void
 */
public static function DB_supprimer_liaison_devoir_prof($devoir_id)
{
  $DB_SQL = 'DELETE FROM sacoche_jointure_devoir_prof '
          . 'WHERE devoir_id=:devoir_id ';
  $DB_VAR = array( ':devoir_id' => $devoir_id );
  DB::query(SACOCHE_STRUCTURE_BD_NAME , $DB_SQL , $DB_VAR);
}

}
?>