diff -urN cahier-de-prepa2.2.0/admin.php cahier-de-prepa3.0.0/admin.php
--- cahier-de-prepa2.2.0/admin.php	2013-01-05 09:42:30.000000000 +0100
+++ cahier-de-prepa3.0.0/admin.php	2013-08-20 01:24:03.686780490 +0200
@@ -10,28 +10,49 @@
 //// HTML ////
 //////////////
 // Haut de page, menu et message
-$p = 'monaccueil';
-$t = 'Mon Cahier de Prépa';
+$p = 'accueil_admin';
+$t = 'Cahier de Prépa - Administration';
 $mysqli = new mysqli($serveur,$base,$mdp,$base);
 $mysqli->set_charset('utf8');
 include('haut.php');
 
-// Récupération de l'identifiant de la matière
-$resultat = $mysqli->query("SELECT id, colles, cdt, docs FROM matieres WHERE cle='${_SESSION['mat']}'");
-$r = $resultat->fetch_assoc();
-$resultat->free();
-$mat = $r['id'];
+// Taille maximale de fichier
+$taille = min(ini_get('upload_max_filesize'),ini_get('post_max_size'));
+if ( stristr($taille,'m') )
+  $taille = substr($taille,0,-1)*1048576;
+elseif ( stristr($taille,'k') )
+  $taille = substr($taille,0,-1)*1024;
+$taille = ( $taille < 1048576 ) ? intval($taille/1024).'&nbsp;ko.' : intval($taille/1048576).'&nbsp;Mo';
 ?>
 
-  <div class="item">
+  <div>
     <p>Bienvenue sur l'interface d'administration de Cahier de Prépa.</p>
     <p>Cette page propose des possibilités de modifications rapide, mais vous pouvez gérer beaucoup plus de choses grâce aux liens du menu&nbsp;!</p>
   </div>
-  
+
+  <div class="item aide">
+    <h3>Aide et explications</h3>
+    <p>Vous pouvez ci-dessous effctuer les tâches quotidiennes les plus fréquentes&nbsp;: ajouter un programme de colle dans les matières qui vous sont associées, déposer un document dans n'importe quel répertoire du site, ou enregistrer une nouvelle information. Tous ces formulaires sont des raccourcis vers les différentes section de cette interface d'administration.</p>
+    <h4>Programmes de colles</h4>
+    <p>La case à cocher <em>Ne pas diffuser sur la partie publique</em> permet de cacher temporairement ce programme de colle, par exemple pour le diffuser ultérieurement.</p>
+    <h4>Document</h4>
+    <p>Le <em>nom à afficher</em> sera le nom réellement affiché sur le site web (sans extension&nbsp;: elle est détectée automatiquement lors de l'envoi). Si vous laissez cette case vide, le nom du fichier envoyé sera récupéré.</p>
+    <p>Vous pouvez choisir la visibilité du document parmi trois possibilités&nbsp;:</p>
+    <ul>
+      <li><em>Visible de tous</em>&nbsp;: document accessible de tout visiteur</li>
+      <li><em>Visible après identification</em>&nbsp; document accessible uniquement après identification, par exemple pour que seuls vos élèves les voient. Il faut alors <a href="utilisateurs">créer un utilisateur de type élève</a>.</li>
+      <li><em>Non visible</em>&nbsp;: le document n'est pas encore visible en ligne. Cela peut être utile si vous souhaitez le mettre à disposition ultérieurement. C'est complètement sûr&nbsp;: vous pouvez mettre comme cela le prochain sujet de devoir ou même le corrigé.</li>
+    </ul>
+    <p>La taille du fichier envoyé est limitée à <?php echo $taille; ?>. Tout document est modifiable et déplaçable, sans que cela ne modifie le lien vers le document.</p>
+    <h4>Information</h4>
+    <p>La case à cocher <em>Ne pas diffuser sur la partie publique</em> permet de cacher temporairement cette information, par exemple pour la diffuser ultérieurement.</p>
+  </div>
 <?php
-////////////////////////////////
-// Nouveau programme de colle //
-////////////////////////////////
+
+///////////////////////////////////
+// Nouveaux programmes de colles //
+///////////////////////////////////
+
 // Fonction d'affichage des semaines
 function format_date($date)  {
   $semaine = array('dimanche','lundi','mardi','mercredi','jeudi','vendredi','samedi');
@@ -39,62 +60,61 @@
   return $semaine[substr($date,0,1)].' '.substr($date,7).' '.$mois[intval(substr($date,5,2))].' '.substr($date,1,4);
 }
 
-// Récupération des semaines
-$resultat = $mysqli->query("SELECT s.id AS sid, DATE_FORMAT(s.debut,'%w%Y%m%e') AS d, s.colle, s.vacances, c.id AS cid, c.cache
-                            FROM semaines AS s
-                            LEFT JOIN (SELECT id, semaine, cache FROM colles WHERE matiere = $mat) AS c ON c.semaine=s.id");
-$select_semaines = '';
-if ( $resultat->num_rows )  {
-  while ( $r = $resultat->fetch_assoc() )  {
-    switch ( $r['vacances'] )  {
-      case 0:
-        if ( $r['colle'] )  {
-          if ( is_null($r['cid']) )
-            $select_semaines .= "\n        <option value=\"${r['sid']}\">".format_date($r['d']).' (à remplir)</option>';
-          elseif ( $r['cache'] )
-            $select_semaines .= "\n        <option value=\"${r['sid']}\">".format_date($r['d']).' (caché)</option>';
+// Pour chaque matière
+foreach ( $matieres as $m )  {
+  // Récupération des semaines
+  $resultat = $mysqli->query("SELECT s.id AS sid, DATE_FORMAT(s.debut,'%w%Y%m%e') AS d, s.colle, s.vacances, c.id AS cid, c.cache
+                              FROM semaines AS s
+                              LEFT JOIN (SELECT id, semaine, cache FROM colles WHERE matiere = ${m['id']}) AS c ON c.semaine=s.id");
+  $select_semaines = '';
+  if ( $resultat->num_rows )  {
+    while ( $r = $resultat->fetch_assoc() )  {
+      switch ( $r['vacances'] )  {
+        case 0:
+          if ( $r['colle'] )  {
+            if ( is_null($r['cid']) )
+              $select_semaines .= "\n        <option value=\"${r['sid']}\">".format_date($r['d']).'</option>';
+            elseif ( $r['cache'] )
+              $select_semaines .= "\n        <option value=\"${r['sid']}\" disabled>".format_date($r['d']).' (non diffusé)</option>';
+            else
+              $select_semaines .= "\n        <option value=\"${r['sid']}\" disabled>".format_date($r['d']).' (déjà rempli)</option>';
+          }
           else
-            $select_semaines .= "\n        <option value=\"${r['sid']}\">".format_date($r['d']).'</option>';
-        }
-        else
-          $select_semaines .= "\n        <option value=\"${r['sid']}\" disabled>".format_date($r['d']).' (pas de colle)</option>';
-        break;
-      case 1:
-        $select_semaines .= "        <option value=\"${r['sid']}\" disabled>Vacances de Toussaint</option>\n";
-        break;
-      case 2:
-        $select_semaines .= "        <option value=\"${r['sid']}\" disabled>Vacances de Noël</option>\n";
-        break;
-      case 3:
-        $select_semaines .= "        <option value=\"${r['sid']}\" disabled>Vacances d'hiver</option>\n";
-        break;
-      case 4:
-        $select_semaines .= "        <option value=\"${r['sid']}\" disabled>Vacances de Pâques</option>\n";
-        break;
+            $select_semaines .= "\n        <option value=\"${r['sid']}\" disabled>".format_date($r['d']).' (pas de colle)</option>';
+          break;
+        case 1:
+          $select_semaines .= "\n        <option value=\"${r['sid']}\" disabled>Vacances de Toussaint</option>";
+          break;
+        case 2:
+          $select_semaines .= "\n        <option value=\"${r['sid']}\" disabled>Vacances de Noël</option>";
+          break;
+        case 3:
+          $select_semaines .= "\n        <option value=\"${r['sid']}\" disabled>Vacances d'hiver</option>";
+          break;
+        case 4:
+          $select_semaines .= "\n        <option value=\"${r['sid']}\" disabled>Vacances de Pâques</option>";
+      }
     }
-  }
-  $resultat->free();
-}
-else  {
-  $mysqli->close();
-  include('installation.php');
-}
+    $resultat->free();
 ?>
 
   <div class="item admin">
-  <form action="colles?<?php echo "${_SESSION['mat']}$urladmin"; ?>" method="post">
+  <form action="colles?<?php echo "${m['cle']}$urladmin"; ?>" method="post">
     <input class="bouton" type="submit" name="modifie" value="Valider" title="Valider les modifications">
-    <h3>Enregistrer un programme de colles</h3>
-    <p class="ligne"><label for="id">Semaine&nbsp;: </label>
-      <select name="id">
-<?php echo $select_semaines; ?>
+    <h3>Enregistrer un programme de colles de <?php echo $m['nom']; ?></h3>
+    <p class="ligne"><label for="id<?php echo $m['id']; ?>">Semaine&nbsp;: </label>
+      <select id="id<?php echo $m['id']; ?>" name="id"><?php echo $select_semaines; ?>
+
       </select>
     </p>
     <textarea name="texte" rows="10" cols="100"></textarea>
-    <p class="ligne"><label for="cache">N'est pas diffusé sur la partie publique&nbsp;: </label><input type="checkbox" id="cache" name="cache" value="1"></p>
+    <p class="ligne"><label for="cache<?php echo $m['id']; ?>">Ne pas diffuser sur la partie publique&nbsp;: </label><input type="checkbox" id="cache<?php echo $m['id']; ?>" name="cache" value="1"></p>
   </form>
   </div>
 <?php
+  }
+}
+
 ///////////////////////
 // Envoi de document //
 ///////////////////////
@@ -110,29 +130,33 @@
 $select_reps = '';
 liste(0,0);
 // Sélection automatique du répertoire racine de la matière
-$resultat = $mysqli->query("SELECT id FROM reps WHERE matiere = $mat AND parent = 0");
+$resultat = $mysqli->query("SELECT id FROM reps WHERE matiere = {$matieres[0]['id']} AND parent = 0");
 $r = $resultat->fetch_assoc();
 $resultat->free();
 $select_reps = str_replace("\"${r['id']}\"","\"${r['id']}\" selected",$select_reps);
+// Choix par défaut de la protection
+$select_protection = str_replace("\"${_SESSION['protection']}\"","\"${_SESSION['protection']}\" selected",'<option value="0">Visible de tous</option><option value="1">Visible après identification</option>');
 ?>
 
   <div class="item admin">
-  <form action="docs_admin" method="post" enctype="multipart/form-data">
+  <form action="docs<?php echo str_replace('&amp;','?',$urladmin); ?>" method="post" enctype="multipart/form-data">
     <input class="bouton" type="submit" name="envoie" value="Envoyer">
-    <h3>Déposer un fichier</h3>
-    <p class="ligne"><label for="nomfichier">Nom à afficher&nbsp;: </label><input type="text" id="nomfichier" name="nom" value="" size="50"></p>
-    <p class="ligne"><label for="fichier">Fichier&nbsp;: </label><input type="file" name="fichier"></p>
+    <h3>Déposer un document</h3>
+    <p class="ligne"><label for="nom">Nom à afficher&nbsp;: </label><input type="text" id="nom" name="nom" value="" size="50"></p>
+    <p class="ligne"><label for="fichier">Fichier&nbsp;: </label><input type="file" id="fichier" name="fichier"></p>
     <p class="ligne"><label for="rep">Répertoire&nbsp;: </label>
       <select id="rep" name="rep">
 <?php echo $select_reps; ?>
       </select>
     </p>
     <p class="ligne"><label for="protection">Accès&nbsp;: </label>
-      <select id="protection" name="protection"><option value="0">Visible de tous</option><option value="1">Avec mot de passe</option><option value="2">Caché</option></select>
+      <select id="protection" name="protection"><?php echo $select_protection; ?><option value="2">Non visible</option></select>
     </p>
+    <input type="hidden" name="id" value="0">
   </form>
   </div>
 <?php
+
 //////////////////////////
 // Nouvelle information //
 //////////////////////////
@@ -154,69 +178,11 @@
     </p>
     <input class="ligne" type="text" name="titre" size=50 maxlength=65533 value="Titre">
     <textarea name="texte" rows="6" cols="100"><p>Texte</p></textarea>
-    <p class="ligne"><label for="cache">Ne pas publier immédiatement (information cachée)&nbsp;: </label><input type="checkbox" id="cache" name="cache" value="1"></p>
-    <input type="hidden" name="id" value="">
-  </form>
-  </div>
-<?php
-//////////////////
-// Mot de passe //
-//////////////////
-// Récupération de l'identifiant de l'utilisateur
-$resultat = $mysqli->query("SELECT id FROM utilisateurs WHERE nom = '${_SESSION['user']}'");
-$r = $resultat->fetch_assoc();
-$resultat->free();
-?>
-
-  <div class="item admin">
-  <form action="utilisateurs" method="post">
-    <input class="bouton" type="submit" name="modifie_mdp" value="Valider" title="Valider les modifications">
-    <h3>Modifier mon mot de passe</h3>
-    <p class="ligne"><label for="mdp0">Mot de passe actuel&nbsp;: </label><input type="password" id="mdp0" name="mdp0" value=""></p>
-    <p class="ligne"><label for="mdp1">Nouveau&nbsp;: </label><input type="password" id="mdp1" name="mdp1" value=""></p>
-    <p class="ligne"><label for="mdp2">Confirmation&nbsp;: </label><input type="password" id="mdp2" name="mdp2" value=""></p>
-    <input type="hidden" name="id" value="<?php echo $r['id']; ?>">
-  </form>
-  </div>
-<?php
-////////////////////////////////////
-// Matière concernée et affichage //
-////////////////////////////////////
-// Récupération des données de la matière
-$resultat = $mysqli->query("SELECT cle, nom, colles, cdt FROM matieres WHERE id = $mat");
-$r = $resultat->fetch_assoc();
-$resultat->free();
-$colles = str_replace("${r['colles']}\"","${r['colles']}\" selected",'<option value="0">Ne pas afficher</option><option value="1">Afficher sans mot de passe</option><option value="2">Afficher avec mot de passe</option>');
-$cdt = str_replace("${r['cdt']}\"","${r['cdt']}\" selected",'<option value="0">Ne pas afficher</option><option value="1">Afficher sans mot de passe</option><option value="2">Afficher avec mot de passe</option>');
-?>
-
-  <div class="item admin">
-  <form action="matieres" method="post">
-    <input class="bouton" type="submit" name="modifie" value="Valider" title="Valider les modifications">
-    <h3>Modifier l'affichage de ma matière</h3>
-    <p class="ligne"><label for="nom">Nom complet&nbsp;: </label><input type="text" id="nom" name="nom" value="<?php echo $r['nom']; ?>" size="50"></p>
-    <p class="ligne"><label for="cle">Clé dans l'adresse&nbsp;: </label><input type="text" id="cle" name="cle" value="<?php echo $r['cle']; ?>" size="30"></p>
-    <p class="ligne"><label for="colles">Programme de colles</label>
-      <select id="colles" name="colles"><?php echo $colles; ?></select>
-    </p>
-    <p class="ligne"><label for="cdt">Cahier de texte</label>
-      <select id="cdt" name="cdt"><?php echo $cdt; ?></select>
-    </p>
-    <input type="hidden" name="id" value="<?php echo $mat; ?>">
+    <p class="ligne"><label for="cache">Ne pas diffuser sur la partie publique&nbsp;: </label><input type="checkbox" id="cache" name="cache" value="1"></p>
+    <input type="hidden" name="id" value="0">
   </form>
   </div>
 
-  <script type="text/javascript">
-$( function() {
-  $('.item h3').append(' <span>[déplier]</span>');
-  $('.item h3 span').css('cursor','pointer').click( function () {
-    $(this).parent().parent().find('.bouton,.ligne,textarea').toggle();
-    $(this).text($(this).text() == '[déplier]' ? '[replier]' : '[déplier]');
-  });
-  $('form').find('.bouton,.ligne,textarea').hide();
-});
-  </script>
-
   <script type="text/javascript" src="textarea.js.php"></script>
 
 <?php
diff -urN cahier-de-prepa2.2.0/bas.php cahier-de-prepa3.0.0/bas.php
--- cahier-de-prepa2.2.0/bas.php	2013-01-04 10:39:25.000000000 +0100
+++ cahier-de-prepa3.0.0/bas.php	2013-08-21 14:50:39.043093958 +0200
@@ -1,18 +1,33 @@
 </div>
 
+<?php
+if ( $admin )  {
+?>
   <script type="text/javascript">
 $( function() {
-  // Pliage des aides de l'interface d'administration
-  $('.aide').prepend("<h3 class=\"deplie\">Aide et explications <span>[déplier]</span></h3>");
-  $('.deplie span').css('cursor','pointer').click( function () {
-    $(this).parent().parent().find('p').toggle();
-    $(this).text($(this).text() == '[déplier]' ? '[replier]' : '[déplier]');
+  // Pliage des items et aides de l'interface d'administration
+  $('.aide h3,.admin h3').append(' <span>déplier</span>');
+  $('.aide h3 span,.admin h3 span').click( function () {
+    $(this).parent().parent().children(':not(h3,.nonvisible)').toggle();
+    $(this).text($(this).text() == 'déplier' ? 'replier' : 'déplier');
+  });
+  // Premier pliage
+  $('.aide,.admin form').children(':not(h3)').hide();
+  // Recopie des boutons de modification d'ordre
+  $( "[name='monte'],[name='descend']" ).each(function(){
+    // Un bouton de validation doit se trouver en premier dans le formulaire pour
+    // valider par appui sur la touche entrée. 
+    // On place derrière le bouton de descente, puis le bouton de montée
+    $(this).prev("[name='modifie']").clone().addClass('nonvisible').hide().prependTo($(this).parents('form'));
+    $(this).clone().addClass('bouton').insertAfter($(this).parents('form').find('.nonvisible'));
   });
-  $('.aide').find('p').hide();
 });
-
   </script>
 
-</div><div id="bas">Ce site est réalisé par le logiciel <a href="http://cahier-de-prepa.fr">Cahier de prépa</a>, publié sous <a href="Licence_CeCILL_V2-fr.html">licence libre</a>.</div>
+<?php
+}
+?>
+</div>
+<div id="bas">Ce site est réalisé par le logiciel <a href="http://cahier-de-prepa.fr">Cahier de prépa</a>, publié sous <a href="Licence_CeCILL_V2-fr.html">licence libre</a>.</div>
 </body>
 </html>
diff -urN cahier-de-prepa2.2.0/cdt.php cahier-de-prepa3.0.0/cdt.php
--- cahier-de-prepa2.2.0/cdt.php	2013-01-05 10:32:59.000000000 +0100
+++ cahier-de-prepa3.0.0/cdt.php	2013-08-20 13:21:07.988157273 +0200
@@ -11,7 +11,7 @@
 // si existe ; sur l'interface d'administration, affichage sans condition)
 $mysqli = premiere_connexion();
 $mysqli->set_charset('utf8');
-$resultat = $mysqli->query('SELECT id, cle, nom, cdt FROM matieres WHERE cdt OR '.var_export($admin,true));
+$resultat = $mysqli->query('SELECT id, cle, nom, cdt FROM matieres WHERE MOD(cdt,2) OR '.var_export($admin,true));
 if ( $resultat->num_rows )  {
   if ( !empty($_REQUEST) )
     while ( $r = $resultat->fetch_assoc() )
@@ -26,9 +26,11 @@
   }
   $resultat->free();
 }
-// Si aucune matière présentant son programme de colles n'est enregistrée
-else
+// Si aucune matière présentant son cahier de texte n'est enregistrée
+else  {
+  $mysqli->close();
   exit('Cette page ne contient aucune information.');
+}
 
 // Fonction d'affichage des semaines
 function format_date($date)  {
@@ -39,25 +41,25 @@
 
 // Récupération des semaines
 $resultat = $mysqli->query("SELECT id, DATE_FORMAT(debut,'%w%Y%m%e') AS debut, vacances FROM semaines");
-$semaines = '';
+$select_semaines = '';
 if ( $resultat->num_rows )  {
   while ( $r = $resultat->fetch_assoc() )  {
     switch ( $r['vacances'] )  {
       case 0:
-        $semaines .= "\n        <option value=\"${r['id']}\">".format_date($r['debut']).'</option>';
+        $select_semaines .= "\n        <option value=\"${r['id']}\">".format_date($r['debut']).'</option>';
         $sid[] = $r['id'];
         break;
       case 1:
-        $semaines .= "\n        <option value=\"${r['id']}\" disabled>Vacances de Toussaint</option>";
+        $select_semaines .= "\n        <option value=\"${r['id']}\" disabled>Vacances de Toussaint</option>";
         break;
       case 2:
-        $semaines .= "\n        <option value=\"${r['id']}\" disabled>Vacances de Noël</option>";
+        $select_semaines .= "\n        <option value=\"${r['id']}\" disabled>Vacances de Noël</option>";
         break;
       case 3:
-        $semaines .= "\n        <option value=\"${r['id']}\" disabled>Vacances d'hiver</option>";
+        $select_semaines .= "\n        <option value=\"${r['id']}\" disabled>Vacances d'hiver</option>";
         break;
       case 4:
-        $semaines .= "\n        <option value=\"${r['id']}\" disabled>Vacances de Pâques</option>";
+        $select_semaines .= "\n        <option value=\"${r['id']}\" disabled>Vacances de Pâques</option>";
         break;
     }
   }
@@ -108,6 +110,18 @@
   $mysqli = new mysqli($serveur,"$base-adm",$mdp,$base);
   $mysqli->set_charset('utf8');
 
+  // Vérification que l'identifiant est valide. Défaut : id=0 (nouvelle entrée)
+  if ( $id )  {
+    $resultat = $mysqli->query("SELECT id FROM cdt WHERE matiere = ${matiere['id']} AND id = $id");
+    if ( $resultat->num_rows )
+      $resultat->free();
+    else
+      $id = 0;
+  }
+
+  // Sauvegarde de la table contenant les données
+  sauvegarde_mysql('cdt');
+  
   // Traitement d'un ajout/modification
   if ( isset($_REQUEST['modifie']) && strlen($_REQUEST['texte']) )  {
     
@@ -125,9 +139,6 @@
         $nb = 1;
       }
       
-      // Sauvegarde de la table contenant les données
-      sauvegarde_mysql('cdt');
-      
       // Validation des autres données envoyées
       $h_debut = preg_replace('/(\d{1,2})h(\d{2})/','$1:$2',$_REQUEST['h_debut']);
       $h_fin = preg_replace('/(\d{1,2})h(\d{2})/','$1:$2',$_REQUEST['h_fin']);
@@ -153,40 +164,28 @@
       }
     }
     else
-      $message = 'La date demandée n\'est pas valable.';
+      $message = 'La date saisie se situe hors de l\'année scolaire.';
   }
-  
-  // Traitement d'une transformation/suppression
-  elseif ( isset($_REQUEST['id']) && is_numeric($id = $_REQUEST['id']) )  {
 
-    // Vérification que l'identifiant est valide
-    $resultat = $mysqli->query("SELECT id FROM cdt WHERE matiere = ${matiere['id']} AND id = $id");
-    if ( $resultat->num_rows ) {
-      $r = $resultat->fetch_assoc();
-      $resultat->free();
-      // Sauvegarde de la table contenant les données
-      sauvegarde_mysql('cdt');
+  elseif ( $id )  {
 
-      // Positionnement "montré" (apparaît publiquement)
-      if ( isset($_REQUEST['montre']) )
-        $message = ( $mysqli->query("UPDATE cdt SET cache = 0 WHERE id = $id")
-        ) ? 'L\'entrée du cahier de texte a bien été réaffichée&nbsp;: elle apparaît à nouveau dans la partie publique.' : 'L\'entrée du cahier de texte n\'a pas pu être réaffichée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'».';
-
-      // Positionnement "caché" (n'apparaît plus publiquement)
-      elseif ( isset($_REQUEST['cache']) )
-        $message = ( $mysqli->query("UPDATE cdt SET cache = 1 WHERE id = $id")
-        ) ? 'L\'entrée du cahier de texte a bien été cachée&nbsp;: elle n\'apparaît plus dans la partie publique.' : 'L\'entrée du cahier de texte n\'a pas pu être cachée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'».';
-
-      // Suppression
-      elseif ( isset($_REQUEST['supprime']) || !strlen($texte) )
-        $message = ( $mysqli->query("DELETE FROM cdt WHERE id = $id")
-        ) ? 'L\'entrée du cahier de texte a bien été supprimée.' : 'L\'entrée du cahier de texte n\'a pas pu être supprimée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'».';
-    }
+    // Positionnement "montré" (apparaît sur la partie publique)
+    if ( isset($_REQUEST['montre']) )
+      $message = ( $mysqli->query("UPDATE cdt SET cache = 0 WHERE id = $id") ) ? 'L\'entrée du cahier de texte a bien été diffusée, elle apparaît désormais sur la partie publique.' : 'L\'entrée du cahier de texte n\'a pas pu être diffusée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'».';
+
+    // Positionnement "caché" (n'apparaît plus sur la partie publique)
+    elseif ( isset($_REQUEST['cache']) )
+      $message = ( $mysqli->query("UPDATE cdt SET cache = 1 WHERE id = $id") ) ? 'L\'entrée du cahier de texte n\'est plus diffusée&nbsp;: elle n\'apparaît plus dans la partie publique mais est toujours disponible ici pour modification ou diffusion.' : 'L\'entrée du cahier de texte n\'a pas pu être cachée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'».';
+
+    // Suppression
+    elseif ( isset($_REQUEST['supprime']) || !strlen($texte) )
+      $message = ( $mysqli->query("DELETE FROM cdt WHERE id = $id") ) ? 'L\'entrée du cahier de texte a bien été supprimée.' : 'L\'entrée du cahier de texte n\'a pas pu être supprimée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'».';
   }
-  
-  // Mise à jour des champs 'cdt' dans la table 'matieres' (pour le menu)
-  $mysqli->query('UPDATE matieres SET cdt = IF((SELECT id FROM cdt WHERE matiere = matieres.id AND cache = 0 LIMIT 1),1,0) WHERE cdt < 2');
     
+  // Mise à jour des champs 'cdt' dans la table 'matieres' (pour le menu)
+  $mysqli->query('UPDATE matieres SET cdt = cdt - MOD(cdt,2) + IF((SELECT id FROM cdt WHERE matiere = matieres.id AND cache = 0 
+  LIMIT 1),1,0)');
+
   // Passage en connexion MySQL en lecture seulement
   $mysqli->close();
   $mysqli = new mysqli($serveur,$base,$mdp,$base);
@@ -199,14 +198,15 @@
 $p = "cdt?${matiere['cle']}";
 $t = "Cahier de texte - ${matiere['nom']}";
 
-// Demander l'authentification si la page est protégée et l'utilisateur non connecté
-if ( ( $matiere['cdt'] == 2 ) && !$admin && !$lecteur )
+// Demander l'identification si la page est protégée et l'utilisateur non connecté
+if ( ( $matiere['cdt'] == 3 ) && !$admin && !$lecteur )
   include('login_lecture.php');
+
 // Haut de page, menu et message
 include('haut.php');
 
 // Formulaire de demande d'affichage
-$semaines = str_replace("\"$n\"","\"$n\" selected",$semaines);
+$select_semaines = str_replace("\"$n\"","\"$n\" selected",$select_semaines);
 $types = $recherche_types = '';
 $resultat = $mysqli->query("SELECT cle FROM `cdt-types` WHERE matiere = ${matiere['id']}");
 if ( isset($_REQUEST['type']) )
@@ -225,6 +225,7 @@
 
 $u = strlen($urladmin) ? "\n    <input type=\"hidden\" name=\"admin\" value=\"\">" : '';
 echo <<<FIN
+
   <div class="item" id="recherche">
   <form action="" method="get">
     <input type="hidden" name="${matiere['cle']}" value="">
@@ -233,7 +234,7 @@
         <option value="tout">tout</option>$types
       </select>
       pendant&nbsp;<input type="text" name="nb" value="$nb" size="2">&nbsp;semaine(s) à partir du&nbsp;
-      <select name="n">$semaines
+      <select name="n">$select_semaines
       </select>
       <input type="submit" name="" value="OK">
     </p>$u
@@ -245,21 +246,23 @@
 
 // Aide générale pour l'interface d'administration
 if ( $admin )  {
-  $u = str_replace('&amp;','?',$urladmin);
-  echo <<<FIN
+?>
   <div class="item aide">
+    <h3>Aide et explications</h3>
     <p>Vous pouvez ci-dessous modifier votre cahier de texte. Vous pouvez choisir les semaines à afficher ci-dessus.</p>
+    <p>La case à cocher <em>Ne pas diffuser sur la partie publique</em> permet de cacher temporairement cette entrée du cahier de texte, par exemple pour le diffuser ultérieurement.</p>
+    <p>Vous pouvez <em>cacher</em> ou <em>montrer</em> une entrée de cahier de texte existante, c'est-à-dire la diffuser ou non sur la partie publique.</p>
+    <p>Vous pouvez supprimer une entrée de cahier de texte existante par le bouton <em>Supprimer</em> ou en validant un texte vide.</p>
     <p>Les semaines sont réglables <a href="semaines">ici</a>.</p>
-    <p>Il est possible de garder caché une entrée, pour prendre de l'avance et l'afficher plus tard par exemple.</p>
-    <p>La page affichant ce cahier de texte peut être protégée par mot de passe. Ceci est réglable <a href=".$u">en bas de votre page d'accueil</a> ou <a href="matiere?${matiere['cle']}$urladmin">sur la page de votre matière</a>.</p>
-    <p>Vous pouvez supprimer une entrée par le bouton <em>Supprimer</em> ou en validant un texte vide.</p>
+    <p>Il est possible de permettre l'accès à la page affichant ce cahier de texte uniquement aux visiteurs qui se sont identifiés. Cela est réglable avec vos <a href="prefs">préférences</a>. Il faut alors <a href="utilisateurs">créer un compte</a> de type élève pour permettre aux élèves d'accéder à cette page.</p>
   </div>
+
   <div class="item">
-    <p>Vous pouvez <a href="cdt-types?${matiere['cle']}$urladmin">modifier les types d'entrées</a> de votre cahier de texte. Vous pouvez aussi <a href="cdt-seances?${matiere['cle']}$urladmin">définir vos séances hebdomadaires</a>, et créer ainsi des boutons de raccourcis qui pré-rempliront les champs type, date, heures et demi-groupe. Toutes ces modifications ne concerneront que votre matière, et n'impacteront pas vos collègues.</p>
+    <p>Vous pouvez <a href="cdt-seances?<?php echo $matiere['cle']; ?>">définir des boutons de raccourci</a>, qui pré-rempliront les champs type, date, heures et demi-groupe. Ils apparaîtront dans chaque formulaire d'entrée du cahier de texte. Ces boutons sont propres à chaque matière.</p>
+    <p>Les types d'entrées du cahier de texte sont aussi <a href="cdt-types?<?php echo $matiere['cle']; ?>">modifiables</a>, indépendamment pour chaque matière.</p>
   </div>
 
-
-FIN;
+<?php
 
   // Select des types d'entrée différent du précédent
   $types = '';
@@ -269,15 +272,6 @@
   $resultat->free();
 }
 
-// Liens de navigation vers semaines précédentes/suivantes
-$nav = '  <div class="item">';
-if ( $m )
-  $nav .= "\n    <a class=\"prec\" href=\"?${matiere['cle']}&amp;n=". $sid[max($m-$nb,0)] ."&amp;nb=$nb$urladmin\">Semaines précédentes</a>";
-if (  $m+$nb < count($sid) )
-  $nav .= "\n    <a class=\"suiv\" href=\"?${matiere['cle']}&amp;n=". $sid[$m+$nb] ."&amp;nb=$nb$urladmin\">Semaines suivantes</a>";
-$nav .= "\n  </div>\n\n";
-echo $nav;
-
 // Fonction d'affichage
 function affichage($r)  {
   $semaine = array('','Lundi','Mardi','Mercredi','Jeudi','Vendredi','Samedi');
@@ -299,20 +293,20 @@
       $suppr = "\n      <input type=\"submit\" name=\"supprime\" value=\"Supprimer\" title=\"Supprimer l'entrée\">";
       $cache_nouveau = '';
       if ( $r['cache'] )  {
-        $debut .= ' (entrée cachée, non présente sur le site public)';
+        $debut .= ' (entrée non diffusée sur la partie publique)';
         $cache_classe = ' cache';
-        $cache = "\n      <input type=\"submit\" name=\"montre\" value=\"Montrer\" title=\"Montrer l'entrée, la rendre visible depuis le site public\">";
+        $cache = "\n      <input type=\"submit\" name=\"montre\" value=\"Montrer\" title=\"Diffuser l'entrée, la rendre visible sur la partie publique\">";
       }
       else  {
         $cache_classe = '';
-        $cache = "\n      <input type=\"submit\" name=\"cache\" value=\"Cacher\" title=\"Cacher l'entrée, la rendre invisible depuis le site public\">";
+        $cache = "\n      <input type=\"submit\" name=\"cache\" value=\"Cacher\" title=\"Ne plus diffuser l'entrée, la rendre invisible sur la partie publique\">";
       }
     }
     else  {
       $debut = 'Nouvelle entrée du cahier';
       $valide1 = "\n    <input class=\"bouton\" type=\"submit\" name=\"modifie\" value=\"Valider\" title=\"Valider les modifications\">";
       $valide = $cache = $cache_classe = $suppr = '';
-      $cache_nouveau = "\n    <p class=\"ligne\"><label for=\"cache\">Ne pas publier immédiatement (entrée cachée)&nbsp;: </label><input type=\"checkbox\" id=\"cache\" name=\"cache\" value=\"1\"></p>";
+      $cache_nouveau = "\n    <p class=\"ligne\"><label for=\"cache\">Ne pas diffuser sur la partie publique&nbsp;: </label><input type=\"checkbox\" id=\"cache\" name=\"cache\" value=\"1\"></p>";
     }
     $demigroupe = ( strlen($r['demigroupe']) ) ? ' checked' : '';
     $types = str_replace("\"${r['tid']}\"","\"${r['tid']}\" selected",$GLOBALS['types']);
@@ -320,7 +314,7 @@
     echo <<<FIN
   <div class="item cdt admin$cache_classe">
   <form action="${GLOBALS['p']}&amp;n=${GLOBALS['n']}&amp;nb=${GLOBALS['nb']}$type${GLOBALS['urladmin']}" method="post">$valide1
-    <p class="titre">$debut</p>
+    <h3>$debut</h3>
     <p class="boutons">$valide$cache$suppr
     </p>
     <p class="ligne"><label for="tid$id">Type&nbsp;:</label>
@@ -378,6 +372,20 @@
   }
 $resultat->free();
 
+// Formulaire vide
+if ( $admin )
+  affichage(array('id' => 0, 'texte' => '&lt;p&gt;Texte&lt;/p&gt;', 'pour' => '00/00/0000', 'date' => date('d/m/Y'),
+                  'h_debut' => '08h00', 'h_fin' => '10h00', 'demigroupe' => '', 'cache' => 0, 'tid' => ''));
+
+// Liens de navigation vers semaines précédentes/suivantes
+$nav = '  <div class="item">';
+if ( $m )
+  $nav .= "\n    <a class=\"prec\" href=\"?${matiere['cle']}&amp;n=". $sid[max($m-$nb,0)] ."&amp;nb=$nb$urladmin\">Semaines précédentes</a>";
+if (  $m+$nb < count($sid) )
+  $nav .= "\n    <a class=\"suiv\" href=\"?${matiere['cle']}&amp;n=". $sid[$m+$nb] ."&amp;nb=$nb$urladmin\">Semaines suivantes</a>";
+$nav .= "\n  </div>\n\n";
+echo $nav;
+
 // Récupération des entrées
 $resultat = $mysqli->query("SELECT cdt.id, cdt.texte, cdt.semaine, DATE_FORMAT(cdt.pour,'%d/%m/%Y') AS pour,
                             DATE_FORMAT(cdt.jour,'%w') AS jour, DATE_FORMAT(cdt.jour,'%d/%m/%Y') AS date,
@@ -419,12 +427,8 @@
     echo "  <h2>Le cahier de texte ne contient pas de «&nbsp;${_REQUEST['type']}&nbsp;» pour les semaines choisies.</h2>\n\n";
 }
 
-// Formulaire vide et javascript pour le pliage/dépliage des items
+// Récupération des boutons
 if ( $admin )  {
-  affichage(array('id' => 0, 'texte' => '&lt;p&gt;Texte&lt;/p&gt;', 'pour' => '00/00/0000', 'date' => date('d/m/Y'),
-                  'h_debut' => '08h00', 'h_fin' => '10h00', 'demigroupe' => '', 'cache' => 0, 'tid' => ''));
-
-  // Récupération des boutons
   $resultat = $mysqli->query("SELECT id, nom, jour, TIME_FORMAT(h_debut,'%Hh%i') AS h_debut, TIME_FORMAT(h_fin,'%Hh%i') AS h_fin,
                               type, demigroupe FROM `cdt-seances` WHERE matiere = ${matiere['id']}");
   $raccourcis = '';
@@ -444,7 +448,7 @@
     t = ${r['jour']}-t;
     if ( t>0 ) { t-=7; }
     \$(this).parent().parent().find('[id^=\"jour\"]').datepick('setDate',t+'d');
-  });";
+  });\n";
     }
     $resultat->free();  
   }
@@ -453,24 +457,12 @@
   <script type="text/javascript" src="textarea.js.php?m=${matiere['id']}"></script>
   
   <script type="text/javascript">
-
 $( function() {
-
   $('<p class="boutons">$raccourcis</p>').clone().insertBefore($('.admin form').find('.ligne:first'));
 $raccourcisjs
-
-  $('.titre').append(' <span>[déplier]</span>');
-  $('.titre span').css('cursor','pointer').click( function () {
-    $(this).parent().parent().find('.bouton,.boutons,.ligne,textarea').toggle();
-    $(this).text($(this).text() == '[déplier]' ? '[replier]' : '[déplier]');
-  });
-  $('.cdt form').find('.bouton,.boutons,.ligne,textarea').hide();
-
   $('.date').datepick({dateFormat: 'dd/mm/yyyy', showTrigger: '<img src="js/calendar-blue.gif">'});
   $('.heure').timeEntry({timeSteps: [1, 30, 0],separator: 'h'});
-
 });
-
   </script>
 
 FIN;
diff -urN cahier-de-prepa2.2.0/cdt-seances.php cahier-de-prepa3.0.0/cdt-seances.php
--- cahier-de-prepa2.2.0/cdt-seances.php	2012-09-13 18:53:08.000000000 +0200
+++ cahier-de-prepa3.0.0/cdt-seances.php	2013-08-20 00:15:01.490647939 +0200
@@ -5,26 +5,20 @@
 include('debut.php');
 
 // Recherche de la matière concernée
-$mysqli = premiere_connexion();
-$mysqli->set_charset('utf8');
-$resultat = $mysqli->query('SELECT id, cle, nom FROM matieres WHERE cdt');
-if ( $resultat->num_rows )  {
-  if ( !empty($_REQUEST) )
-    while ( $r = $resultat->fetch_assoc() )
-      if ( isset($_REQUEST[$r['cle']]) )  {
-        $matiere = $r;
-        break;
-      }
-  // Matière par défaut : la première
-  if ( !isset($matiere) )  {
-    $resultat->data_seek(0);
-    $matiere = $resultat->fetch_assoc();
-  }
+if ( !empty($_REQUEST) )  {
+  $mysqli = premiere_connexion();
+  $mysqli->set_charset('utf8');
+  $resultat = $mysqli->query('SELECT id, cle, nom FROM matieres');
+  while ( $r = $resultat->fetch_assoc() )
+    if ( isset($_REQUEST[$r['cle']]) )  {
+      $matiere = $r;
+      break;
+    }
   $resultat->free();
 }
-// Si aucune matière présentant son programme de colles n'est enregistrée
-else
-  exit('Cette page ne contient aucune information.');
+// Si mauvaise demande
+if ( !isset($matiere) )
+  exit('Mauvais paramètre d\'accès à cette page');
 
 ///////////////////
 // Modifications //
@@ -54,14 +48,14 @@
       if ( $id )
         $message = ( $mysqli->query("UPDATE `cdt-seances` SET nom = '$nom', jour = $jour, h_debut = '$h_debut', h_fin = '$h_fin',
                                      type = $type, demigroupe = $demigroupe WHERE id = $id")
-        ) ? 'Le raccourci «&nbsp;'.stripslashes($nom).'&nbsp;» a bien été modifiée.' : 'Le raccourci «&nbsp;'.stripslashes($nom).'&nbsp;» n\'a pas pu être modifiée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'».';
+        ) ? 'Le raccourci <em>'.stripslashes($nom).'</em> a bien été modifiée.' : 'Le raccourci <em>'.stripslashes($nom).'</em> n\'a pas pu être modifiée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'».';
       // Sinon, nouveau raccourci
       else  {
         $message = ( $mysqli->query("INSERT INTO `cdt-seances` SET matiere = ${matiere['id']},
                                      ordre = (SELECT IFNULL(max(cs.ordre)+1,1) FROM `cdt-seances` AS cs WHERE cs.matiere = ${matiere['id']}),
                                      nom = '$nom', jour = $jour, h_debut = '$h_debut', h_fin = '$h_fin', type = $type, demigroupe = $demigroupe")
                   && $mysqli->query('ALTER TABLE `cdt-seances` ORDER BY ordre,matiere')
-        ) ? 'Le raccourci «&nbsp;'.stripslashes($nom).'&nbsp;» a bien été ajouté.' : 'Le raccourci «&nbsp;'.stripslashes($nom).'&nbsp;» n\'a pas pu être ajouté. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'».';
+        ) ? 'Le raccourci <em>'.stripslashes($nom).'</em> a bien été ajouté.' : 'Le raccourci <em>'.stripslashes($nom).'</em> n\'a pas pu être ajouté. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'».';
       }
     }
   }
@@ -84,19 +78,19 @@
       if ( isset($_REQUEST['monte']) && ( $ordre > 1 ) )
         $message = ( $mysqli->query("UPDATE `cdt-seances` SET ordre = (2*$ordre-1-ordre) WHERE ( ordre = $ordre OR ordre = ($ordre-1) ) AND matiere = ${matiere['id']}")
                   && $mysqli->query('ALTER TABLE `cdt-seances` ORDER BY ordre,matiere')
-        ) ? "Le raccourci «&nbsp;${r['nom']}&nbsp;» a bien été monté d'une place." : "Le raccourci «&nbsp;${r['nom']}&nbsp;» n'a pas pu être déplacé. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'».';
+        ) ? "Le raccourci <em>${r['nom']}</em> a bien été monté d'une place." : "Le raccourci <em>${r['nom']}</em> n'a pas pu être déplacé. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'».';
 
       // Déplacement vers le bas
       elseif ( isset($_REQUEST['descend']) && ( $ordre < $r['max'] ) )
         $message = ( $mysqli->query("UPDATE `cdt-seances` SET ordre = (2*$ordre+1-ordre) WHERE ( ordre = $ordre OR ordre = ($ordre+1) ) AND matiere = ${matiere['id']}")
                   && $mysqli->query('ALTER TABLE `cdt-seances` ORDER BY ordre,matiere')
-        ) ? "Le raccourci «&nbsp;${r['nom']}&nbsp;» a bien été descendu d'une place." : "Le raccourci «&nbsp;${r['nom']}&nbsp;» n'a pas pu être déplacé. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'».';
+        ) ? "Le raccourci <em>${r['nom']}</em> a bien été descendu d'une place." : "Le raccourci <em>${r['nom']}</em> n'a pas pu être déplacé. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'».';
 
       // Suppression
       elseif ( isset($_REQUEST['supprime']) )
         $message =  ( $mysqli->query("DELETE FROM `cdt-seances` WHERE id = $id")
                    && $mysqli->query("UPDATE `cdt-seances` SET ordre = (ordre-1) WHERE ordre > $ordre AND matiere = ${matiere['id']}")
-        ) ? "Le raccourci «&nbsp;${r['nom']}&nbsp;» a bien été supprimé." : "Le raccourci «&nbsp;${r['nom']}&nbsp;» n'a pas pu être supprimé. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'».';
+        ) ? "Le raccourci <em>${r['nom']}</em> a bien été supprimé." : "Le raccourci <em>${r['nom']}</em> n'a pas pu être supprimé. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'».';
     }
   }
 
@@ -110,24 +104,26 @@
 //// HTML ////
 //////////////
 $p = "cdt?${matiere['cle']}";
-$t = "Modification du cahier de texte - ${matiere['nom']}";
+$t = "Modification des raccourcis du cahier de texte - ${matiere['nom']}";
 // Haut de page, menu et message
 include('haut.php');
 
 // Aide générale
-echo <<<FIN
+?>
 
-  <div class="item aide">
-    <p>Vous pouvez ci-dessous modifier les différentes séances de votre semaine, les déplacer, les supprimer. L'intérêt est que chaque séance correspondra, sur vos page de saisie du cahier de texte, à un bouton de raccourci qui saisira automatiquement les champs type, date, heures et demi-groupe. Ces modifications sont propres à votre matière.</p>
-    <p>Le <em>nom</em> sera affiché sur le bouton. L'ordre définit leur ordre d'apparition.</p>
+  <div>
+    <a href="cdt?<?php echo $matiere['cle'].$urladmin; ?>">Revenir au cahier de texte de <?php echo $matiere['nom']; ?></a>
   </div>
-  <div class="item">
-    <p>Vous pouvez aussi <a href="cdt-types?${matiere['cle']}$urladmin">modifier les types d'entrées</a> de votre cahier de texte. Pour revenir à votre cahier de texte, <a href="cdt?${matiere['cle']}$urladmin">c'est par ici&nbsp;!</a></p>
-  </div>
-
 
-FIN;
+  <div class="item aide">
+    <h3>Aide et explications</h3>
+    <p>Vous pouvez créer et modifier ici les boutons de raccourci qui apparaissent dans les formulaires d'entrée du cahier de texte.</p>
+    <p>Le <em>nom</em> sera affiché sur le bouton. Leur ordre d'apparition est modifiable grâce aux flèches.</p>
+    <p>Ces boutons sont propres à la matière <?php echo $matiere['nom']; ?>.</p>
+    <p>Lors de la saisie d'une entrée du cahier de texte, cliquer sur un bouton de raccourci modifiera automatiquement les valeurs spécifiées (type d'entrée, date, heure de début, éventuellement heure de fin, demi-groupe).</p>
+  </div>
 
+<?php
 // Selects pour les formulaires
 $semaine = '
         <option value="1">Lundi</option>
@@ -147,7 +143,7 @@
 // Fonction d'affichage
 function affichage($r)  {
   if ( $id = $r['id'] )  {
-    $debut = "Raccourci n°${r['ordre']}";
+    $debut = "Raccourci n°${r['ordre']}&nbsp;: ${r['nom']}";
     $valide1 = '';
     $valide = "\n      <input type=\"submit\" name=\"modifie\" value=\"Valider\" title=\"Valider les modifications\">";
     $monte = ( $r['ordre'] == 1 ) ? '' : "\n      <input type=\"submit\" name=\"monte\" value=\"&uarr;\" title=\"Remonter le raccourci dans l'ordre d'apparition\">";
diff -urN cahier-de-prepa2.2.0/cdt-types.php cahier-de-prepa3.0.0/cdt-types.php
--- cahier-de-prepa2.2.0/cdt-types.php	2012-09-13 18:53:54.000000000 +0200
+++ cahier-de-prepa3.0.0/cdt-types.php	2013-08-20 00:14:05.806646157 +0200
@@ -5,26 +5,20 @@
 include('debut.php');
 
 // Recherche de la matière concernée
-$mysqli = premiere_connexion();
-$mysqli->set_charset('utf8');
-$resultat = $mysqli->query('SELECT id, cle, nom FROM matieres WHERE cdt');
-if ( $resultat->num_rows )  {
-  if ( !empty($_REQUEST) )
-    while ( $r = $resultat->fetch_assoc() )
-      if ( isset($_REQUEST[$r['cle']]) )  {
-        $matiere = $r;
-        break;
-      }
-  // Matière par défaut : la première
-  if ( !isset($matiere) )  {
-    $resultat->data_seek(0);
-    $matiere = $resultat->fetch_assoc();
-  }
+if ( !empty($_REQUEST) )  {
+  $mysqli = premiere_connexion();
+  $mysqli->set_charset('utf8');
+  $resultat = $mysqli->query('SELECT id, cle, nom FROM matieres');
+  while ( $r = $resultat->fetch_assoc() )
+    if ( isset($_REQUEST[$r['cle']]) )  {
+      $matiere = $r;
+      break;
+    }
   $resultat->free();
 }
-// Si aucune matière présentant son programme de colles n'est enregistrée
-else
-  exit('Cette page ne contient aucune information.');
+// Si mauvaise demande
+if ( !isset($matiere) )
+  exit('Mauvais paramètre d\'accès à cette page');
 
 ///////////////////
 // Modifications //
@@ -50,14 +44,14 @@
       // Si identifiant, modification d'un type existant
       if ( $id )
         $message = ( $mysqli->query("UPDATE `cdt-types` SET titre = '$titre', cle = '$cle', h_fin = $h_fin WHERE id = $id")
-        ) ? 'Le type «&nbsp;'.stripslashes($titre).'&nbsp;» a bien été modifiée.' : 'Le type «&nbsp;'.stripslashes($titre).'&nbsp;» n\'a pas pu être modifiée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'».';
+        ) ? 'Le type <em>'.stripslashes($titre).'</em> a bien été modifiée.' : 'Le type <em>'.stripslashes($titre).'</em> n\'a pas pu être modifiée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'».';
       // Sinon, nouveau type
       else  {
         $message = ( $mysqli->query("INSERT INTO `cdt-types` SET matiere = ${matiere['id']},
                                      ordre = (SELECT max(ct.ordre)+1 FROM `cdt-types` AS ct WHERE ct.matiere = ${matiere['id']}),
                                      titre = '$titre', cle = '$cle', h_fin = $h_fin")
                   && $mysqli->query('ALTER TABLE `cdt-types` ORDER BY ordre,matiere')
-        ) ? 'Le type «&nbsp;'.stripslashes($titre).'&nbsp;» a bien été ajouté.' : 'Le type «&nbsp;'.stripslashes($titre).'&nbsp;» n\'a pas pu être ajouté. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'».';
+        ) ? 'Le type <em>'.stripslashes($titre).'</em> a bien été ajouté.' : 'Le type <em>'.stripslashes($titre).'</em> n\'a pas pu être ajouté. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'».';
       }
     }
   }
@@ -80,13 +74,13 @@
       if ( isset($_REQUEST['monte']) && ( $ordre > 1 ) )
         $message = ( $mysqli->query("UPDATE `cdt-types` SET ordre = (2*$ordre-1-ordre) WHERE ( ordre = $ordre OR ordre = ($ordre-1) ) AND matiere = ${matiere['id']}")
                   && $mysqli->query('ALTER TABLE `cdt-types` ORDER BY ordre,matiere')
-        ) ? "Le type «&nbsp;${r['titre']}&nbsp;» a bien été monté d'une place." : "Le type «&nbsp;${r['titre']}&nbsp;» n'a pas pu être déplacé. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'».';
+        ) ? "Le type <em>${r['titre']}</em> a bien été monté d'une place." : "Le type <em>${r['titre']}</em> n'a pas pu être déplacé. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'».';
 
       // Déplacement vers le bas
       elseif ( isset($_REQUEST['descend']) && ( $ordre < $r['max'] ) )
         $message = ( $mysqli->query("UPDATE `cdt-types` SET ordre = (2*$ordre+1-ordre) WHERE ( ordre = $ordre OR ordre = ($ordre+1) ) AND matiere = ${matiere['id']}")
                   && $mysqli->query('ALTER TABLE `cdt-types` ORDER BY ordre,matiere')
-        ) ? "Le type «&nbsp;${r['titre']}&nbsp;» a bien été descendu d'une place." : "Le type «&nbsp;${r['titre']}&nbsp;» n'a pas pu être déplacé. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'».';
+        ) ? "Le type <em>${r['titre']}</em> a bien été descendu d'une place." : "Le type <em>${r['titre']}</em> n'a pas pu être déplacé. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'».';
 
       // Suppression
       elseif ( isset($_REQUEST['supprime']) && ( $r['max'] > 1 ) )  {
@@ -94,12 +88,12 @@
         $resultat = $mysqli->query("SELECT id FROM cdt WHERE type = $id AND matiere = ${matiere['id']}");
         if ( $resultat->num_rows )  {
           $resultat->free();
-          $message = "Le type «&nbsp;${r['titre']}&nbsp;» n'a pas pu être supprimé car des entrées du cahier de texte sont de ce type. Il faut d'abord les supprimer.";
+          $message = "Le type <em>${r['titre']}</em> n'a pas pu être supprimé car des entrées du cahier de texte sont de ce type. Il faut d'abord les supprimer.";
         }
         else
           $message =  ( $mysqli->query("DELETE FROM `cdt-types` WHERE id = $id")
                      && $mysqli->query("UPDATE `cdt-types` SET ordre = (ordre-1) WHERE ordre > $ordre AND matiere = ${matiere['id']}")
-          ) ? "Le type «&nbsp;${r['titre']}&nbsp;» a bien été supprimé." : "Le type «&nbsp;${r['titre']}&nbsp;» n'a pas pu être supprimé. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'».';
+          ) ? "Le type <em>${r['titre']}</em> a bien été supprimé." : "Le type <em>${r['titre']}</em> n'a pas pu être supprimé. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'».';
       }
     }
   }
@@ -114,31 +108,32 @@
 //// HTML ////
 //////////////
 $p = "cdt?${matiere['cle']}";
-$t = "Modification du cahier de texte - ${matiere['nom']}";
+$t = "Modification des types du cahier de texte - ${matiere['nom']}";
 // Haut de page, menu et message
 include('haut.php');
 
 // Aide générale
-echo <<<FIN
+?>
+
+  <div>
+    <a href="cdt?<?php echo $matiere['cle'].$urladmin; ?>">Revenir au cahier de texte de <?php echo $matiere['nom']; ?></a>
+  </div>
 
   <div class="item aide">
-    <p>Vous pouvez ci-dessous modifier les différents types d'entrée de votre cahier de texte, les déplacer, les supprimer. Ces modifications sont propres à votre matière.</p>
+    <h3>Aide et explications</h3>
+    <p>Vous pouvez modifier ici les types d'entrée du cahier de texte. Ces modifications sont propres à la matière <?php echo $matiere['nom']; ?>.</p>
     <p>Le <em>titre</em> sera affiché au début de chaque entrée. Il doit s'agit d'un nom singulier et commençant par une majuscule. Il peut être relativement long. Par exemple&nbsp;: «&nbsp;Séance de travaux pratiques&nbsp;», «&nbsp;Interrogation de cours&nbsp;»</p>
-    <p>La <em>clé</em> sera affichée dans le menu déroulant de recherche, précédé de «&nbsp;les&nbsp», ainsi que dans l'adresse des pages qui affichent ce type d'entrée&nbsp;: il faut donc que ce soit un pluriel, court, en un mot, sans majucule au début (sauf s'il le faut). Par exemple, «&nbsp;TP&nbsp;», «&nbsp;interros&nbsp;»</p>
-    <p>Si la case <em>Affiche l'heure de fin</em> est décoché, les entrées de ce type n'auront qu'une heure de début&nbsp;: ce peut être le cas pour les événements qui ne durent pas (interrogations de cours par exemple).</p>
-    <p>Pour supprimer un type, il faut qu'aucune entrée de ce type n'existe dans le cahier de texte.</p>
-  </div>
-  <div class="item">
-    <p>Vous pouvez aussi <a href="cdt-seances?${matiere['cle']}$urladmin">définir vos séances hebdomadaires</a>, et créer ainsi des boutons de raccourcis qui pré-rempliront les champs type, date, heures et demi-groupe. Pour revenir à votre cahier de texte, <a href="cdt?${matiere['cle']}$urladmin">c'est par ici&nbsp;!</a></p>
+    <p>La <em>clé</em> sera affichée dans le menu déroulant de recherche, précédé de «&nbsp;les&nbsp», ainsi que dans l'adresse des pages qui affichent ce type d'entrée&nbsp;: il faut donc que ce soit un pluriel, court, en un mot, sans majucule au début (sauf s'il le faut). Par exemple, «&nbsp;TP&nbsp;», «&nbsp;interros&nbsp;».</p>
+    <p>Si la case <em>Affiche une heure de fin</em> est décochée, les entrées de ce type n'auront qu'une heure de début&nbsp;: ce peut être le cas pour les événements qui ne durent pas (interrogations de cours, devoirs maison par exemple).</p>
+    <p>Pour supprimer un type, il est nécessaire qu'aucune entrée de ce type n'existe dans le cahier de texte.</p>
   </div>
 
-
-FIN;
+<?php
 
 // Fonction d'affichage
 function affichage($r)  {
   if ( $id = $r['id'] )  {
-    $debut = "Type n°${r['ordre']}";
+    $debut = "Type n°${r['ordre']}&nbsp;: ${r['titre']}";
     if ( $r['n'] )
       $debut .= ( $r['n'] > 1 ) ? " (${r['n']} entrées dans le cahier de texte)" : " (${r['n']} entrée dans le cahier de texte)";
     $valide1 = '';
@@ -160,7 +155,7 @@
     </p>
     <p class="ligne"><label for="titre$id">Titre (singulier)&nbsp;: </label><input type="input" id="titre$id" name="titre" value="${r['titre']}" size="50"></p>
     <p class="ligne"><label for="cle$id">Clé (pluriel)&nbsp;: </label><input type="input" id="cle$id" name="cle" value="${r['cle']}" size="50"></p>
-    <p class="ligne"><label for="h_fin$id">Affiche une heure de fin de séance&nbsp;: </label><input type="checkbox" id="h_fin$id" name="h_fin" value="1"${r['h_fin']}></p>
+    <p class="ligne"><label for="h_fin$id">Affiche une heure de fin&nbsp;: </label><input type="checkbox" id="h_fin$id" name="h_fin" value="1"${r['h_fin']}></p>
     <input type="hidden" name="id" value="$id">
   </form>
   </div>
@@ -175,7 +170,7 @@
 affichage(array('id' => 0, 'titre' => '', 'cle' => '', 'h_fin' => ''));
 
 // Affichage des types
-$resultat = $mysqli->query("SELECT max(ordre) FROM `cdt-types` WHERE matiere = ${matiere['id']}");
+$resultat = $mysqli->query("SELECT MAX(ordre) FROM `cdt-types` WHERE matiere = ${matiere['id']}");
 $r = $resultat->fetch_row();
 $max = $r[0];
 $resultat->free();
diff -urN cahier-de-prepa2.2.0/CHANGELOG.php cahier-de-prepa3.0.0/CHANGELOG.php
--- cahier-de-prepa2.2.0/CHANGELOG.php	2013-01-11 19:02:45.000000000 +0100
+++ cahier-de-prepa3.0.0/CHANGELOG.php	2013-08-22 18:01:55.590226019 +0200
@@ -1,5 +1,5 @@
 <?php exit; ?>
-Version actuelle : 2.2.0 (11/01/13)
+Version actuelle : 3.0.0 (23/08/13)
 ===================
 Changements :
 1.0   31/08/11 Première version
@@ -54,33 +54,36 @@
         administrative (admin.php, cdp.php, colles.php, index.php)
         * un avertissement s'affiche pour prévenir la déconnexion et permet la reconnexion
         sans recharger la page (donc sans perdre les données)
-
+3.0.0 23/08/13 Réécriture d'une partie du moteur interne. Ajout de nouvelles fonctionnalités :
+        * nouvelle gestion des informations récentes, dans une table séparée, affichées
+        sur le côté dans la partie publique
+        * flux RSS disponible pour les informations récentes
+        * modifications dans l'interface d'administration : possibilité de monter/descendre
+        des items plus facilement ; simplification globale ; aide réécrite et complétée
+        * plusieurs matières peuvent être affichées dans le menu de l'interface d'administration
+        * gestion des documents facilité : possibilité de mettre à jour un document
+        * ajout d'une page de préférences utilisateur, possibilité de valider par défaut 
+        la protection par mot de passe des documents
+        * clarification de la création et utilisation de compte utilisateur de type élève
+        * initialisation possible du planning des semaines
+        Correction du bug concernant l'enregistrement de document commençant par un accent
 ===================
 
-[ 2.3 ] Février ou Avril 2013
- * suppression multiple d'informations
- * correction bug infos auto si modification (ex: doc envoyé, l'info auto n'est pas
-   modifiée s'il est déplacé/renommé)
- * news sur le côté
- * nouvelle gestion des infos automatiques
- * FAQ
-   -> comment ajouter un collègue ou un compte élèves
+[ 3.1 ] Décembre 2013
+ * vraie version mobile
+
+[ 3.2 ] Février 2014
+ * vérification des saisies de texte et aide au formatage en HTML, ajout automatique
+ de balises <p>, suppression des fin de lignes et lignes vides finales
+ * choix du regroupement dans le cahier de texte, par séance, par jour, par semaine
 
-[ 3.0 ] Août 2013
+[ 3.3 ] Avril 2014
+ * suppression multiple d'informations
  * exportation des données
- * vérification des saisie de texte, ajout automatique de balises <p>, éventuellement
-        de balises fermantes oubliées ?, ou simplification de la saisie,
-        suppression des fin de lignes et lignes vides finales
- * flux RSS
- * choix du regroupement dans le cahier de texte, par séance ou par jour
+ 
+[ 4.0 ] Août 2014
+ * gestion des informations récentes : choix des notifications, suppressions/modifications
+ * accès protégé hors élèves (profs et colleurs)
  * choix du regroupement des colles, par semaine ou quinzaine
- * archives des années précédentes
-
-[ 3.0 ou supérieures ]
- * changement de styles pour les titres
- * changement des couleurs
- * génération de compte et mot de passe en lecture pour les élèves
- * intégration des répertoires et documents envoyés sans utiliser le site web
  * gestion des sauvegardes automatiques : élimination des anciennes, exportation
- * exportation/importation des données
-  
+ * paramétrage des styles pour les titres, des couleurs
diff -urN cahier-de-prepa2.2.0/colles.php cahier-de-prepa3.0.0/colles.php
--- cahier-de-prepa2.2.0/colles.php	2013-01-03 23:48:08.000000000 +0100
+++ cahier-de-prepa3.0.0/colles.php	2013-08-23 10:27:58.252119232 +0200
@@ -7,7 +7,7 @@
 // si existe ; sur l'interface d'administration, affichage sans condition)
 $mysqli = premiere_connexion();
 $mysqli->set_charset('utf8');
-$resultat = $mysqli->query('SELECT id, cle, nom, colles FROM matieres WHERE colles OR '.var_export($admin,true));
+$resultat = $mysqli->query('SELECT id, cle, nom, colles FROM matieres WHERE MOD(colles,2) OR '.var_export($admin,true));
 if ( $resultat->num_rows )  {
   if ( !empty($_REQUEST) )
     while ( $r = $resultat->fetch_assoc() )
@@ -23,8 +23,10 @@
   $resultat->free();
 }
 // Si aucune matière présentant son programme de colles n'est enregistrée
-else
+else  {
+  $mysqli->close();
   exit('Cette page ne contient aucune information.');
+}
 
 ///////////////////
 // Modifications //
@@ -35,129 +37,93 @@
   $mysqli = new mysqli($serveur,"$base-adm",$mdp,$base);
   $mysqli->set_charset('utf8');
 
-  // Vérification que l'identifiant est valide
-  $resultat = $mysqli->query("SELECT DATE_FORMAT(s.debut,'%e/%m/%Y') AS debut, c.id, c.cache FROM semaines AS s
+  // Vérification que l'identifiant de la semaine est valide
+  $resultat = $mysqli->query("SELECT DATE_FORMAT(s.debut,'%e/%m') AS debut, c.id, c.cache, c.texte FROM semaines AS s
                               LEFT JOIN (SELECT id, texte, semaine, cache FROM colles WHERE matiere = ${matiere['id']}) AS c ON c.semaine=s.id
                               WHERE s.id = $id AND s.colle = 1");
   if ( $resultat->num_rows ) {
     $r = $resultat->fetch_assoc();
     $cid = $r['id'];
     $resultat->free();
+    $_REQUEST['n'] = $id;
+    
     // Sauvegarde de la table contenant les données
     sauvegarde_mysql('colles');
 
+    // Pour les informations récentes
+    $titre_recent = "Colles du ${r['debut']} en ".addslashes($matiere['nom']);
+    $lien_recent = 'colles?'.addslashes($matiere['cle'])."&amp;n=$id";
+
     // Ajout d'un nouveau programme de colles. Il ne faut pas pouvoir insérer un programme vide par erreur
-    if ( is_null($cid) && strlen($_REQUEST['texte']) )  {
+    if ( isset($_REQUEST['modifie']) && strlen($_REQUEST['texte']) )  {
+      
       // Validation des données envoyées
       $texte = $mysqli->real_escape_string($_REQUEST['texte']);
-      $cache = ( isset($_REQUEST['cache']) ) ? 1 : 0;
-      if ( $mysqli->query("INSERT INTO colles SET texte = '$texte', semaine = $id, matiere = ${matiere['id']}, cache = $cache") && ( $cid = $mysqli->insert_id ) )  {
-        $message = "Le programme de colles de la semaine du ${r['debut']} a bien été ajouté.";
-        // Ajout d'une information en page d'accueil si programme non caché
-        if ( !$cache )  {
-          // Sauvegarde de la table contenant les données
-          sauvegarde_mysql('infos');
-          $message .= ( $mysqli->query('UPDATE infos SET ordre = (ordre+1) WHERE page = 0') 
-                     && $mysqli->query("INSERT INTO infos SET ordre = 1, page = 0, auto = 'colle-$cid', cache = 0, texte = '<p>Nouveau programme de colles en ".addslashes($matiere['nom']).' pour la semaine du <a href="colles?'.addslashes($matiere['cle'])."&amp;n=$id\">${r['debut']}</a></p>', titre = '".date('d/m/y').'&nbsp;: nouveau programme de colles\'')
-                     && $mysqli->query('ALTER TABLE infos ORDER BY ordre,page')
-          ) ? "<br>Un lien vers ce programme de colles a été ajouté en haut de la page d'accueil. Ce lien est <a href=\".?accueil$urladmin\">modifiable/supprimable.</a>." : '';
+      
+      // Si $cid n'est pas NULL : modification d'un programme existant. Sinon, nouveau programme
+      if ( $cid )  {
+        if ( $mysqli->query("UPDATE colles SET texte = '$texte' WHERE id = $cid") )  {
+          $message = "Le programme de colles de la semaine du ${r['debut']} a bien été modifié.";
+          // Ajout d'une information récente si programme diffusé
+          if ( !$r['cache'] )
+            recent($mysqli,2,$cid,$titre_recent,$lien_recent,$texte);
         }
+        else
+          $message = "Le programme de colles de la semaine du ${r['debut']} n'a pas pu être modifié. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'».';
       }
-      else
-        $message = "Le programme de colles de la semaine du ${r['debut']} n'a pas pu être ajouté. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'».';
-    }
-
-    // Positionnement "montré" (apparaît publiquement)
-    elseif ( isset($_REQUEST['montre']) )  {
-      if ( $mysqli->query("UPDATE colles SET cache = 0 WHERE id = $cid") )  {
-        $message = 'Le programme de colles a bien été réaffiché&nbsp;: il apparaît à nouveau dans la partie publique.';
-        // Sauvegarde de la table contenant les données
-        sauvegarde_mysql('infos');
-        // Sauvegarde de la table contenant les données
-        sauvegarde_mysql('infos');
-        $message .= ( $mysqli->query('UPDATE infos SET ordre = (ordre+1) WHERE page = 0') 
-                   && $mysqli->query("INSERT INTO infos SET ordre = 1, page = 0, auto = 'colle-$cid', cache = 0, texte = '<p>Nouveau programme de colles en ".addslashes($matiere['nom']).' pour la semaine du <a href="colles?'.addslashes($matiere['cle'])."&amp;n=$id\">${r['debut']}</a></p>', titre = '".date('d/m/y').'&nbsp;: nouveau programme de colles\'')
-                   && $mysqli->query('ALTER TABLE infos ORDER BY ordre,page')
-        ) ? "<br>Un lien vers ce programme de colles a été ajouté en haut de la page d'accueil. Ce lien est <a href=\".?accueil$urladmin\">modifiable/supprimable.</a>." : '';
+      else  {
+        $cache = ( isset($_REQUEST['cache']) ) ? 1 : 0;
+        if ( $mysqli->query("INSERT INTO colles SET texte = '$texte', semaine = $id, matiere = ${matiere['id']}, cache = $cache") )  {
+          $message = "Le programme de colles de la semaine du ${r['debut']} a bien été ajouté.";
+          // Ajout d'information récente si programme diffusé
+          if ( !$cache )
+            recent($mysqli,2,$mysqli->insert_id,$titre_recent,$lien_recent,$texte);
+          $mysqli->query('ALTER TABLE colles ORDER BY matiere,semaine');
+        }
+        else
+          $message = "Le programme de colles de la semaine du ${r['debut']} n'a pas pu être ajouté. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'».';
       }
-      else
-        $message = "Le programme de colles de la semaine du ${r['debut']} n'a pas pu être réaffiché. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'».';
     }
 
-    // Positionnement "caché" (n'apparaît plus publiquement)
-    elseif ( isset($_REQUEST['cache']) )  {
-      if ( $mysqli->query("UPDATE colles SET cache = 1 WHERE id = $cid") )  {
-        $message = "Le programme de colles du ${r['debut']} a bien été caché&nbsp;: il n'apparaît plus dans la partie publique mais est toujours disponible ici pour modification ou réaffichage.";
-        // Suppression de l'éventuelle information en page d'accueil
-        $resultat = $mysqli->query("SELECT id, ordre FROM infos WHERE auto = 'colle-$cid'");
-        if ( $resultat->num_rows )  {
-          $r = $resultat->fetch_assoc();
-          $resultat->free();
-          $message .= ( $mysqli->query("DELETE FROM infos WHERE id = '${r['id']}'")
-                     && $mysqli->query("UPDATE infos SET ordre = (ordre-1) WHERE ordre > ${r['ordre']} AND page = 0")
-          ) ? '<br>Le lien existant depuis la page d\'accueil a été automatiquement supprimé.' : '';
+    elseif ( $cid )  {
+
+      // Positionnement "montré" (apparaît sur la partie publique)
+      if ( isset($_REQUEST['montre']) )  {
+        if ( $mysqli->query("UPDATE colles SET cache = 0 WHERE id = $cid") )  {
+          $message = "Le programme de colles de la semaine du ${r['debut']} a bien été diffusé, il apparaît désormais sur la partie publique.";
+          // Ajout d'information récente
+          recent($mysqli,2,$cid,$titre_recent,$lien_recent,$mysqli->real_escape_string($r['texte']));
         }
+        else
+          $message = "Le programme de colles de la semaine du ${r['debut']} n'a pas pu être diffusé. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'».';
       }
-      else
-        $message = "Le programme de colles de la semaine du ${r['debut']} n'a pas pu être caché. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'».';
-    }
 
-    // Suppression
-    elseif ( isset($_REQUEST['supprime']) || !strlen($_REQUEST['texte']) )  {
-      if ( $mysqli->query("DELETE FROM colles WHERE id = $cid") )  {
-        $message = "Le programme de colles de la semaine du ${r['debut']} a bien été supprimé.";
-        // Sauvegarde de la table contenant les données
-        sauvegarde_mysql('infos');
-        // Suppression de l'information en page d'accueil
-        $resultat = $mysqli->query("SELECT id, ordre FROM infos WHERE auto = 'colle-$cid'");
-        if ( $resultat->num_rows ) {
-          $r = $resultat->fetch_assoc();
-          $resultat->free();
-          $message .= ( $mysqli->query("DELETE FROM infos WHERE id = '${r['id']}'")
-                     && $mysqli->query("UPDATE infos SET ordre = (ordre-1) WHERE ordre > ${r['ordre']} AND page = 0")
-          ) ? '<br>Le lien existant depuis la page d\'accueil a aussi été supprimé.' : '';
+      // Positionnement "caché" (n'apparaît pas sur la partie publique)
+      elseif ( isset($_REQUEST['cache']) )  {
+        if ( $mysqli->query("UPDATE colles SET cache = 1 WHERE id = $cid") )  {
+          $message = "Le programme de colles du ${r['debut']} n'est plus diffusé&nbsp;: il n'apparaît plus sur la partie publique mais est toujours disponible ici pour modification ou diffusion.";
+          // Suppression de l'éventuelle information récente
+          recent($mysqli,2,$cid);
         }
+        else
+          $message = "Le programme de colles de la semaine du ${r['debut']} n'a pas pu être caché. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'».';
       }
-      else
-        $message = "Le programme de colles de la semaine du ${r['debut']} n'a pas pu être modifié. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'».';
-    }
-    
-    // Modification du texte d'un programme de colles existant
-    else  {
-      $texte = $mysqli->real_escape_string($_REQUEST['texte']);
-      if ( $mysqli->query("UPDATE colles SET texte = '$texte' WHERE id = $cid") )  {
-        $message = "Le programme de colles de la semaine du ${r['debut']} a bien été modifié.";
-        // Ajout d'une information en page d'accueil
-        if ( !$r['cache'] )  {
-          // Sauvegarde de la table contenant les données
-          sauvegarde_mysql('infos');
-          $texte = '<p>Modification du programme de colles en '.addslashes($matiere['nom']).' pour la semaine du <a href="colles?'.addslashes($matiere['cle'])."&amp;n=$id\">${r['debut']}</a></p>";
-          $titre = date('d/m/y').'&nbsp;: modification d\\\'un programme de colles';
-          $resultat = $mysqli->query("SELECT id, ordre FROM infos WHERE auto = 'colle-$cid'");
-          // Si elle existe déjà, on la met à jour et on la remonte
-          if ( $resultat->num_rows ) {
-            $r = $resultat->fetch_assoc();
-            $resultat->free();
-            $message .= ( $mysqli->query("UPDATE infos SET ordre = (ordre+1) WHERE page = 0 AND ordre < ${r['ordre']}")
-                       && $mysqli->query("UPDATE infos SET ordre = 1, texte = '$texte', titre = '$titre' WHERE id = ${r['id']}")
-                       && $mysqli->query('ALTER TABLE infos ORDER BY ordre,page')
-            ) ? "<br>Le lien sur la page d'accueil vers ce programme de colles a été remonté en haut de la page. Ce lien est <a href=\".?accueil$urladmin\">modifiable/supprimable.</a>." : '';
-          }
-          else
-            $message .= ( $mysqli->query('UPDATE infos SET ordre = (ordre+1) WHERE page = 0')
-                       && $mysqli->query("INSERT INTO infos SET ordre = 1, page = 0, cache = 0, texte='$texte', titre='$titre', auto='colle-$cid'")
-                       && $mysqli->query('ALTER TABLE infos ORDER BY ordre,page')
-            ) ? "<br>Un lien vers ce programme de colles a été ajouté en haut de la page d'accueil. Ce lien est <a href=\".?accueil$urladmin\">modifiable/supprimable.</a>." : '';
+
+      // Suppression
+      elseif ( isset($_REQUEST['supprime']) || !strlen($_REQUEST['texte']) )  {
+        if ( $mysqli->query("DELETE FROM colles WHERE id = $cid") )  {
+          $message = "Le programme de colles de la semaine du ${r['debut']} a bien été supprimé.";
+          // Suppression de l'éventuelle information récente
+          recent($mysqli,2,$cid);
         }
+        else
+          $message = "Le programme de colles de la semaine du ${r['debut']} n'a pas pu être modifié. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'».';
       }
-      else
-        $message = "Le programme de colles de la semaine du ${r['debut']} n'a pas pu être modifié. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'».';
     }
-
   }
   
   // Mise à jour des champs 'colles' dans la table 'matieres' (pour le menu)
-  $mysqli->query('UPDATE matieres SET colles = IF((SELECT id FROM colles WHERE matiere = matieres.id AND cache = 0 LIMIT 1),1,0) WHERE colles < 2');
+  $mysqli->query('UPDATE matieres SET colles = colles - MOD(colles,2) + IF((SELECT id FROM colles WHERE matiere = matieres.id AND cache = 0 LIMIT 1),1,0)');
 
   // Passage en connexion MySQL en lecture seulement
   $mysqli->close();
@@ -165,18 +131,9 @@
   $mysqli->set_charset('utf8');
 }
 
-////////////
-/// HTML ///
-////////////
-$p = "colles?${matiere['cle']}";
-$t = "Programme de colles - ${matiere['nom']}";
-
-// Demander l'authentification si la page est protégée et l'utilisateur non connecté
-if ( ( $matiere['colles'] == 2 ) && !$admin && !$lecteur )
-  include('login_lecture.php');
-
-// Haut de page, menu et message
-include('haut.php');
+//////////////////////////////////////
+// Validation de la requête : n, nb //
+//////////////////////////////////////
 
 // Fonction d'affichage des semaines
 function format_date($date)  {
@@ -189,7 +146,7 @@
 $resultat = $mysqli->query("SELECT s.id AS sid, DATE_FORMAT(s.debut,'%w%Y%m%e') AS d, s.colle, s.vacances, c.id AS cid, c.cache
                             FROM semaines AS s
                             LEFT JOIN (SELECT id, semaine, cache FROM colles WHERE matiere = ${matiere['id']}) AS c ON c.semaine=s.id");
-$semaines = '';
+$select_semaines = '';
 if ( $resultat->num_rows )  {
   while ( $r = $resultat->fetch_assoc() )  {
     switch ( $r['vacances'] )  {
@@ -197,31 +154,30 @@
         if ( $admin )  {
           if ( $r['colle'] )  {
             if ( is_null($r['cid']) )
-              $semaines .= "\n        <option value=\"${r['sid']}\">".format_date($r['d']).' (à remplir)</option>';
+              $select_semaines .= "\n        <option value=\"${r['sid']}\">".format_date($r['d']).'</option>';
             elseif ( $r['cache'] )
-              $semaines .= "\n        <option value=\"${r['sid']}\">".format_date($r['d']).' (caché)</option>';
+              $select_semaines .= "\n        <option value=\"${r['sid']}\">".format_date($r['d']).' (non diffusé)</option>';
             else
-              $semaines .= "\n        <option value=\"${r['sid']}\">".format_date($r['d']).'</option>';
+              $select_semaines .= "\n        <option value=\"${r['sid']}\">".format_date($r['d']).' (déjà rempli)</option>';
           }
           else
-            $semaines .= "\n        <option value=\"${r['sid']}\" disabled>".format_date($r['d']).' (pas de colle)</option>';
+            $select_semaines .= "\n        <option value=\"${r['sid']}\" disabled>".format_date($r['d']).' (pas de colle)</option>';
         }
         else
-          $semaines .= ( is_null($r['cid']) || $r['cache'] ) ? "\n        <option value=\"${r['sid']}\" disabled>".format_date($r['d']).'</option>' : "\n        <option value=\"${r['sid']}\">".format_date($r['d']).'</option>';
+          $select_semaines .= ( is_null($r['cid']) || $r['cache'] ) ? "\n        <option value=\"${r['sid']}\" disabled>".format_date($r['d']).'</option>' : "\n        <option value=\"${r['sid']}\">".format_date($r['d']).'</option>';
         $sid[] = $r['sid'];
         break;
       case 1:
-        $semaines .= "\n        <option value=\"${r['sid']}\" disabled>Vacances de Toussaint</option>";
+        $select_semaines .= "\n        <option value=\"${r['sid']}\" disabled>Vacances de Toussaint</option>";
         break;
       case 2:
-        $semaines .= "\n        <option value=\"${r['sid']}\" disabled>Vacances de Noël</option>";
+        $select_semaines .= "\n        <option value=\"${r['sid']}\" disabled>Vacances de Noël</option>";
         break;
       case 3:
-        $semaines .= "\n        <option value=\"${r['sid']}\" disabled>Vacances d'hiver</option>";
+        $select_semaines .= "\n        <option value=\"${r['sid']}\" disabled>Vacances d'hiver</option>";
         break;
       case 4:
-        $semaines .= "\n        <option value=\"${r['sid']}\" disabled>Vacances de Pâques</option>";
-        break;
+        $select_semaines .= "\n        <option value=\"${r['sid']}\" disabled>Vacances de Pâques</option>";
     }
   }
   $resultat->free();
@@ -255,26 +211,40 @@
 $m = array_search($n,$sid);
 
 // Validation de nb (nombre de semaines à voir)
-if ( isset($_REQUEST['nb']) && is_numeric($nb = $_REQUEST['nb']) )  {
+if ( isset($_REQUEST['nb']) && is_numeric($nb = $_REQUEST['nb']) ) {
   if ( $nb < 1 )
     $nb = 1;
-  if ( $nb > count($sid) - $m )
-    $nb = count($sid) - $m;
 }
 else
   // Par défaut : affichage de la semaine courante en partie publique, affichage
   // aussi des semaines précédente et suivante sur l'interface d'administration
   $nb = 1 + 2*$admin;
+if ( $nb > count($sid) - $m )
+  $nb = count($sid) - $m;
+
+////////////
+/// HTML ///
+////////////
+$p = "colles?${matiere['cle']}";
+$t = "Programme de colles - ${matiere['nom']}";
+
+// Demander l'identification si la page est protégée et l'utilisateur non connecté
+if ( ( $matiere['colles'] == 3 ) && !$admin && !$lecteur )
+  include('login_lecture.php');
+
+// Haut de page, menu et message
+include('haut.php');
 
 // Formulaire de demande d'affichage
-$semaines = str_replace("\"$n\"","\"$n\" selected",$semaines);
+$select_semaines = str_replace("\"$n\"","\"$n\" selected",$select_semaines);
 $u = strlen($urladmin) ? "\n    <input type=\"hidden\" name=\"admin\" value=\"\">" : '';
 echo <<<FIN
+
   <div class="item" id="recherche">
   <form action="" method="get">
     <input type="hidden" name="${matiere['cle']}" value="">
     <p>Afficher&nbsp;<input type="text" name="nb" value="$nb" size="2">&nbsp;semaine(s) à partir du&nbsp;
-      <select name="n">$semaines
+      <select name="n">$select_semaines
       </select>
       <input type="submit" name="" value="OK">
     </p>$u
@@ -286,18 +256,18 @@
 
 // Aide générale pour l'interface d'administration
 if ( $admin )  {
-  $u = str_replace('&amp;','?',$urladmin);
-  echo <<<FIN
+?>
   <div class="item aide">
+    <h3>Aide et explications</h3>
     <p>Vous pouvez ci-dessous modifier les programmes de colles. Vous pouvez choisir les semaines à afficher ci-dessus.</p>
+    <p>La case à cocher <em>Ne pas diffuser sur la partie publique</em> permet de cacher temporairement ce programme de colle, par exemple pour le diffuser ultérieurement.</p>
+    <p>Vous pouvez <em>cacher</em> ou <em>montrer</em> un programme de colles existant, c'est-à-dire le diffuser ou non sur la partie publique.</p>
+    <p>Vous pouvez supprimer un programme de colles existant par le bouton <em>Supprimer</em> ou en validant un texte vide.</p>
     <p>Les semaines sont réglables <a href="semaines">ici</a>.</p>
-    <p>Il est possible de garder caché un programme de colles (pour le valider plus tard par exemple).</p>
-    <p>La page affichant ces programmes de colles peut être protégé par mot de passe. Ceci est réglable <a href=".$u">en bas de votre page d'accueil</a> ou <a href="matiere?${matiere['cle']}$urladmin">sur la page de votre matière</a>.</p>
-    <p>Vous pouvez supprimer un programme de colles par le bouton <em>Supprimer</em> ou en validant un texte vide.</p>
-    <p>À chaque nouveau programme de colle non caché (ou lorsqu'un programme de colles passe de «&nbsp;caché&nbsp;» à «&nbsp;montré&nbsp;»), une information est automatiquement écrite en haut de la page d'accueil. Elle est <a href=".?accueil$urladmin">modifiable</a>.</p>
+    <p>Il est possible de permettre l'accès à la page affichant ces programmes de colles uniquement aux visiteurs qui se sont identifiés. Cela est réglable avec vos <a href="prefs">préférences</a>. Il faut alors <a href="utilisateurs">créer un compte</a> de type élève pour permettre aux élèves d'accéder à cette page.</p>
   </div>
-  
-FIN;
+
+<?php
 }
 
 // Affichage des semaines concernées
@@ -313,8 +283,8 @@
   if ( $nb > 1 )
     echo $nav;
 
-  // Récupération des données à afficher
   if ( $admin )
+  // Récupération des données à afficher
     $resultat = $mysqli->query("SELECT DATE_FORMAT(s.debut,'%w%Y%m%e') AS d, s.colle, s.vacances, c.texte, s.id, IF(c.cache,' checked','') AS cache 
                                 FROM semaines AS s
                                 LEFT JOIN (SELECT texte, semaine, cache FROM colles WHERE matiere = ${matiere['id']}) AS c ON c.semaine=s.id
@@ -348,19 +318,20 @@
     if ( $admin )  {
       if ( $r['colle'] )  {
         if ( is_null($r['texte']) )  {
+          $titre .= ' (à remplir)';
           $valide1 = "\n    <input class=\"bouton\" type=\"submit\" name=\"modifie\" value=\"Valider\" title=\"Valider les modifications\">";
           $classe_cache = $boutons = '';
-          $cache = "\n    <p class=\"ligne\"><label for=\"cache\">N'est pas diffusé sur la partie publique&nbsp;: </label><input type=\"checkbox\" id=\"cache\" name=\"cache\" value=\"1\"></p>";
+          $cache = "\n    <p class=\"ligne\"><label for=\"cache\">Ne pas diffuser sur la partie publique&nbsp;: </label><input type=\"checkbox\" id=\"cache\" name=\"cache\" value=\"1\"></p>";
         }
         else  {
           if ( $r['cache'] )  {
             $valide1 = $cache = '';
             $classe_cache = ' cache';
-            $titre .= ' (caché, non visible sur le site public)';
+            $titre .= ' (programme non diffusé sur la partie publique)';
             $boutons = "
     <p class=\"boutons\">
       <input type=\"submit\" name=\"modifie\" value=\"Valider\" title=\"Valider les modifications sur le texte\">
-      <input type=\"submit\" name=\"montre\" value=\"Montrer\" title=\"Montrer le programme, le rendre visible depuis le site public\">
+      <input type=\"submit\" name=\"montre\" value=\"Montrer\" title=\"Diffuser le programme, le rendre visible sur la partie publique\">
       <input type=\"submit\" name=\"supprime\" value=\"Supprimer\" title=\"Supprimer le programme de colles\">
     </p>";
           }
@@ -369,7 +340,7 @@
             $boutons = "
     <p class=\"boutons\">
       <input type=\"submit\" name=\"modifie\" value=\"Valider\" title=\"Valider les modifications sur le texte\">
-      <input type=\"submit\" name=\"cache\" value=\"Cacher\" title=\"Cacher le programme, le rendre invisible depuis le site public (le programme sera encore visible ici, par exemple pour un affichage ultérieur)\">
+      <input type=\"submit\" name=\"cache\" value=\"Cacher\" title=\"Ne plus diffuser le programme, le rendre invisible sur la partie public\">
       <input type=\"submit\" name=\"supprime\" value=\"Supprimer\" title=\"Supprimer le programme de colles\">
     </p>";
           }
diff -urN cahier-de-prepa2.2.0/css/couleurs.css cahier-de-prepa3.0.0/css/couleurs.css
--- cahier-de-prepa2.2.0/css/couleurs.css	2012-09-13 17:30:25.000000000 +0200
+++ cahier-de-prepa3.0.0/css/couleurs.css	2013-08-18 10:19:13.238278378 +0200
@@ -14,10 +14,10 @@
 .admin, .cdt, .colle, #recherche, #parents { border: 1px solid #16210B; }
 
 /* Menu */
-#menu { background-color: #CCDDFF; }
-#menu h3 { background-color: #7F97C6; color: #1D3159; }
-#menu a, #menu p { color: #2C4B88; }
-#menu a:hover { color: #7F97C6; }
+#menu, #recent { background-color: #CCDDFF; }
+#menu h3, #recent h3 { background-color: #7F97C6; color: #1D3159; }
+#menu a, #menu p, #recent a { color: #2C4B88; }
+#menu a:hover, #recent a:hover { color: #7F97C6; }
 #menu div+div { border-top: 1px solid #1D3159; }
 
 /* Interface d'administration */
diff -urN cahier-de-prepa2.2.0/css/style.css cahier-de-prepa3.0.0/css/style.css
--- cahier-de-prepa2.2.0/css/style.css	2013-01-05 10:40:41.000000000 +0100
+++ cahier-de-prepa3.0.0/css/style.css	2013-08-21 16:32:53.931290275 +0200
@@ -15,8 +15,10 @@
 ul { margin: 0.5em 2%; padding: 0 4% 0 6%; }
 p { margin: 0.5em 2%; padding: 0 4%; }
 div, form { margin: 0; padding: 0; }
-#menu { width: 250px; float: left; margin: 0 30px 3em; padding: 1em 20px; }
-#contenu { position: relative; margin: 0 30px 4em 350px; padding: 0 20px; }
+#colonne { width: 280px; float: left; margin: -1em 30px 3em; }
+#menu, #recent { padding: 1em 20px; }
+#recent { margin-top: 2em; }
+#contenu { position: relative; margin: 0 30px 4em 340px; padding: 0 20px; }
 #bas { font-size: 0.8em; text-align: center; width: 100%; position: relative; left: 0; bottom: 0; margin-top: -3em; height: 3em; padding: 1em 0; clear: both; }
 
 /* Styles particuliers */
@@ -27,29 +29,31 @@
 .suiv { font-size: 0.8em; margin: 0 3%; float: right; }
 
 /* Liens non soulignés */
-.prec, .suiv, h3 a, #menu a, #bas a { text-decoration: none; }
+.prec, .suiv, h3 a, #menu a, #recent a, #bas a { text-decoration: none; }
 
 /* Ombres : propriétés CSS3 incomprises d'IE8 et inférieurs */
-#menu, .admin, .cdt, .colle {
+#menu, #recent, .admin, .cdt, .colle {
   box-shadow: 0.5em 0.5em 0.5em #777;
   -moz-box-shadow: 0.5em 0.5em 0.5em #777;
   -webkit-box-shadow: 0.5em 0.5em 0.5em #777;
 }
-#menu h3 {
+#menu h3, #recent h3 {
   box-shadow: 0.3em 0.3em 0.3em #777;
   -moz-box-shadow: 0.3em 0.3em 0.3em #777;
   -webkit-box-shadow: 0.3em 0.3em 0.3em #777;
 }
 
-/* Dans le menu */
+/* Dans le menu et dans les informations récentes */
 #menu div+div { margin-top: 0.3em; padding-top: 0.5em; }
-#menu h3 { text-align: center; margin: 0 5% 0.5em; padding: 0.2em 5px; }
+#menu h3, #recent h3 { text-align: center; margin: 0 5% 0.5em; padding: 0.2em 5px; }
 #menu h4 { text-align: center; margin: 0; }
-#menu a { display: block; margin-bottom: 0.2em; padding: 0; }
+#menu a, #recent a { display: block; margin-bottom: 0.2em; padding: 0; }
 #menu a.ligne { display: inline !important; }
 #menu p.menusupp { margin: 0 3% 0.3em; font-size: 0.9em; }
 #menu p { padding: 0.1em 0px; }
 #actuel { font-style: italic; }
+#rss { display: inline !important; margin: 0 !important; vertical-align: top; }
+#rss img { height: 1.1em; }
 
 /* Alignements des formulaires */
 input, select, textarea, #bas {
@@ -73,15 +77,19 @@
 div.rep { margin: 0 0 0 2%; padding: 0; }
 p.rep { margin: 0.5em 0 0.5em 2%; padding: 0; }
 .fic { margin: 0.5em 0 0.5em 4%; padding: 0; }
+.fic form { padding: 0 1% 0.5em; }
 .icone { height: 1.4em; max-width: 64px; vertical-align: middle; }
 .rep a, .fic a, #parents a { font-weight: 700; }
 .icone, .rep a, .fic a, #parents a, p.rep input { margin-right: 0.8em; }
-.fic form { padding: 0 1% 1em; }
 
 /* Interface d'administration */
 #deconnexion { text-align: center; margin-bottom: 1em; }
 .aide { padding-top: 0.1em; }
+.aide + .aide { margin-top: -1em; }
 .bouton { margin: 0 4% 0 10px; float: right; }
+.bouton + .bouton { margin-right: 0; }
+.admin + h2 { margin-top: 1em; }
+.admin h3 span, .aide h3 span { cursor: pointer; font-size: 0.9em; padding: 0 0.2em; border: 1px solid black; margin-left: 1em; }
 #aide_js p, p.boutons, p.ligne, p.symboles { padding: 0; width: 92%; margin: 0.5em 4% 0.2em; }
 input.ligne, textarea { width: 92%; margin: 0.5em 4% 0.2em; }
 p.ligne label { font-weight: 700; vertical-align: sub; }
diff -urN cahier-de-prepa2.2.0/debut.php cahier-de-prepa3.0.0/debut.php
--- cahier-de-prepa2.2.0/debut.php	2013-01-11 16:13:25.000000000 +0100
+++ cahier-de-prepa3.0.0/debut.php	2013-08-20 10:16:39.923803093 +0200
@@ -64,7 +64,7 @@
     // Tout est ok : session valide pour 15 minutes encore
     else  {
       $_SESSION['time'] = time()+900;
-      session_write_close();
+      // Remarque : pas de session_write_close() pour mettre à jour par prefs.php
     }
   }
   
@@ -72,6 +72,7 @@
   if ( !$admin )
     include('login_admin.php');
 
+  /////////////////////////////////////////////
   // Fonction de sauvegarde des données MySQL
   function sauvegarde_mysql($table)  {
     if ( !is_dir('sauvegarde') || !is_executable('sauvegarde') || !is_writable('sauvegarde') )
@@ -124,8 +125,78 @@
     fclose($fichier);
   }
   
+  ///////////////////////////////////////////
+  // Fonction de mise à jour des nouveautés
+  function recent($mysqli,$type,$id,$titre='',$lien='',$texte='')  {
+    // type : 1->informations, 2->programmes de colles, 3->documents
+    // id : celui de l'information/le programme de colles/le document
+
+    // Sauvegarde de la table contenant les données
+    sauvegarde_mysql('recents');    
+
+    // Ajout dans la base de données, suppression si $titre est vide
+    if ( strlen($titre) )
+      $mysqli->query('REPLACE INTO recents SET id = '.($type*1000+$id).", heure = NOW(), titre = '$titre', lien = '$lien', texte = '$texte'");
+    else
+      $mysqli->query('DELETE FROM recents WHERE id = '.($type*1000+$id));
+    
+    // Pas la peine d'aller plus loin si la table n'a pas été modifiée.
+    if ( !($mysqli->affected_rows) )
+      return;
+
+    // Nettoyage des anciennes entrées
+    $mysqli->query('DELETE FROM recents WHERE DATEDIFF(NOW(),heure) > 30');
+    // Mise dans l'ordre, les plus récents en premier
+    $mysqli->query('ALTER TABLE recents ORDER BY heure DESC');
+    
+    // Regénération du flux RSS
+    $rep = 'documents/'.sha1($GLOBALS['base']);
+    $d = date(DATE_RSS);
+    $rss = <<<FIN
+<?xml version="1.0" encoding="UTF-8"?>
+<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
+  <channel>
+    <title>Cahier de prépa - ${GLOBALS['classe']} ${GLOBALS['lycee']}</title>
+    <atom:link href="http://${GLOBALS['site']}/$rep/rss.xml" rel="self" type="application/rss+xml" />
+    <link>http://${GLOBALS['site']}</link>
+    <description>Le site de la ${GLOBALS['classe']} du lycée ${GLOBALS['lycee']}</description>
+    <lastBuildDate>$d</lastBuildDate>
+    <language>fr-FR</language>
+
+
+FIN;
+    // Récupération des items
+    $resultat = $mysqli->query('SELECT UNIX_TIMESTAMP(heure) AS heure, titre, lien, texte FROM recents');
+    while ( $r = $resultat->fetch_assoc() )  {
+      $d = date(DATE_RSS,$r['heure']);
+      $rss .= <<<FIN
+    <item>
+      <title><![CDATA[${r['titre']}]]></title>
+      <link>http://${GLOBALS['site']}/${r['lien']}</link>
+      <guid isPermaLink="false">${r['heure']}</guid>
+      <description><![CDATA[${r['texte']}]]></description>
+      <pubDate>$d</pubDate>
+    </item>
+
+
+FIN;
+    }
+    $rss .= <<<FIN
+  </channel>
+</rss>
+
+FIN;
+
+    // Mise à jour du flux RSS
+    if ( !is_dir($rep) )  mkdir($rep);
+    $fichier = fopen($rep.'/rss.xml','wb');
+    fwrite($fichier, $rss);
+    fclose($fichier);
+  }
+  
   // Paramètre supplémentaire "&admin" à passer dans les urls si $siteadmin est vide
   $urladmin = ( strlen($siteadmin) ) ? '' : '&amp;admin';
+  $lecteur = true;
 }
 
 /////////////////////////////////////////////////
diff -urN cahier-de-prepa2.2.0/definition_semaines.php cahier-de-prepa3.0.0/definition_semaines.php
--- cahier-de-prepa2.2.0/definition_semaines.php	1970-01-01 01:00:00.000000000 +0100
+++ cahier-de-prepa3.0.0/definition_semaines.php	2013-08-23 03:22:47.447302883 +0200
@@ -0,0 +1,39 @@
+<?php
+switch ( $_REQUEST['zone'] )  {
+  case 'A':
+    $semaines = <<<FIN
+INSERT INTO semaines (debut, colle, vacances) VALUES
+  ('2013-09-03',0,0),('2013-09-09',0,0),('2013-09-16',0,0),('2013-09-23',1,0),('2013-09-30',1,0),('2013-10-07',1,0),('2013-10-14',1,0),
+  ('2013-10-21',0,1),('2013-11-04',1,0),('2013-11-12',1,0),('2013-11-18',1,0),('2013-11-25',1,0),('2013-12-02',1,0),('2013-12-09',1,0),
+  ('2013-12-16',1,0),('2013-12-23',0,2),('2014-01-06',1,0),('2014-01-13',1,0),('2014-01-20',1,0),('2014-01-27',1,0),('2014-02-03',1,0),
+  ('2014-02-10',1,0),('2014-02-17',1,0),('2014-02-24',1,0),('2014-03-03',0,3),('2014-03-17',1,0),('2014-03-24',1,0),('2014-03-31',1,0),
+  ('2014-04-07',1,0),('2014-04-14',1,0),('2014-04-22',1,0),('2014-04-28',0,4),('2014-05-12',1,0),('2014-05-20',1,0),('2014-05-26',1,0),
+  ('2014-06-02',1,0),('2014-06-10',1,0),('2014-06-16',1,0),('2014-06-23',1,0),('2014-06-30',1,0);
+
+FIN;
+    break;
+  case 'B':
+    $semaines = <<<FIN
+INSERT INTO semaines (debut, colle, vacances) VALUES
+  ('2013-09-03',0,0),('2013-09-09',0,0),('2013-09-16',0,0),('2013-09-23',1,0),('2013-09-30',1,0),('2013-10-07',1,0),('2013-10-14',1,0),
+  ('2013-10-21',0,1),('2013-11-04',1,0),('2013-11-12',1,0),('2013-11-18',1,0),('2013-11-25',1,0),('2013-12-02',1,0),('2013-12-09',1,0),
+  ('2013-12-16',1,0),('2013-12-23',0,2),('2014-01-06',1,0),('2014-01-13',1,0),('2014-01-20',1,0),('2014-01-27',1,0),('2014-02-03',1,0),
+  ('2014-02-10',1,0),('2014-02-17',1,0),('2014-02-24',0,3),('2014-03-10',1,0),('2014-03-17',1,0),('2014-03-24',1,0),('2014-03-31',1,0),
+  ('2014-04-07',1,0),('2014-04-14',1,0),('2014-04-21',0,4),('2014-05-05',1,0),('2014-05-12',1,0),('2014-05-20',1,0),('2014-05-26',1,0),
+  ('2014-06-02',1,0),('2014-06-10',1,0),('2014-06-16',1,0),('2014-06-23',1,0),('2014-06-30',1,0);
+
+FIN;
+    break;
+  case 'C':
+    $semaines = <<<FIN
+INSERT INTO semaines (debut, colle, vacances) VALUES
+  ('2013-09-03',0,0),('2013-09-09',0,0),('2013-09-16',0,0),('2013-09-23',1,0),('2013-09-30',1,0),('2013-10-07',1,0),('2013-10-14',1,0),
+  ('2013-10-21',0,1),('2013-11-04',1,0),('2013-11-12',1,0),('2013-11-18',1,0),('2013-11-25',1,0),('2013-12-02',1,0),('2013-12-09',1,0),
+  ('2013-12-16',1,0),('2013-12-23',0,2),('2014-01-06',1,0),('2014-01-13',1,0),('2014-01-20',1,0),('2014-01-27',1,0),('2014-02-03',1,0),
+  ('2014-02-10',1,0),('2014-02-17',0,3),('2014-03-03',1,0),('2014-03-10',1,0),('2014-03-17',1,0),('2014-03-24',1,0),('2014-03-31',1,0),
+  ('2014-04-07',1,0),('2014-04-14',0,4),('2014-04-28',1,0),('2014-05-05',1,0),('2014-05-12',1,0),('2014-05-20',1,0),('2014-05-26',1,0),
+  ('2014-06-02',1,0),('2014-06-10',1,0),('2014-06-16',1,0),('2014-06-23',1,0),('2014-06-30',1,0);
+
+FIN;
+}
+?>
diff -urN cahier-de-prepa2.2.0/docs_admin.php cahier-de-prepa3.0.0/docs_admin.php
--- cahier-de-prepa2.2.0/docs_admin.php	2013-01-04 14:51:14.000000000 +0100
+++ cahier-de-prepa3.0.0/docs_admin.php	1970-01-01 01:00:00.000000000 +0100
@@ -1,600 +0,0 @@
-<?php
-// Sécurité et récupération des données de configuration
-// Mode admin obligatoire (OK=2)
-define('OK',2);
-include('debut.php');
-
-// Récupération de l'identifiant de répertoire demandé
-$mysqli = premiere_connexion();
-if ( isset($_REQUEST['rep']) && is_numeric($_REQUEST['rep']) )
-  $rid = $_REQUEST['rep'];
-else  {
-  // Si une matière est demandée
-  if ( !empty($_REQUEST) )  {
-    $resultat = $mysqli->query('SELECT r.id, m.cle FROM reps AS r LEFT JOIN matieres AS m ON r.matiere = m.id');
-    if ( $resultat->num_rows )  {
-      while ( $r = $resultat->fetch_assoc() )
-      if ( isset($_REQUEST[$r['cle']]) )  {
-        $rid = $r['id'];
-        break;
-      }
-      $resultat->free();
-    }
-  }
-  // Valeur par défaut
-  if ( !isset($rid) )
-    $rid = 1;
-}
-
-///////////////////
-// Modifications //
-///////////////////
-if ( isset($_REQUEST['nom']) || isset($_REQUEST['id']) )  {
-  // Connexion à la base de données
-  $mysqli->close();
-  $mysqli = new mysqli($serveur,"$base-adm",$mdp,$base);
-  $mysqli->set_charset('utf8');
-
-  // Récupération des données du répertoire
-  $resultat = $mysqli->query("SELECT nom, parent, protection FROM reps WHERE id = $rid");
-  $rep = $resultat->fetch_assoc();
-  $resultat->free();
-
-  // Récupération des liens de parenté entre répertoires
-  $resultat = $mysqli->query("SELECT id, parents FROM reps");
-  while ( $r = $resultat->fetch_assoc() )
-    $parents[$r['id']] = $r['parents'];
-  $resultat->free();
-
-  // Traitement d'une modification du répertoire
-  if ( isset($_REQUEST['modifie']) )  {
-    // Sauvegarde de la table contenant les données
-    sauvegarde_mysql('reps');
-    $message = '';
-
-    // Modification du nom
-    if ( strlen($nom = $_REQUEST['nom']) && ( $rep['nom'] != $nom ) )  {
-      $nom = $mysqli->real_escape_string($_REQUEST['nom']);
-      $message = ( $mysqli->query("UPDATE reps SET nom = '$nom' WHERE id = $rid")
-                && $mysqli->query('ALTER TABLE reps ORDER BY parents,nom')
-      ) ? 'Le nom du répertoire a bien été modifié.' : 'Le nom du répertoire n\'a pas pu être modifié. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'».';
-    }
-
-    // Traitement du déplacement du répertoire
-    // Impossible si $rep[parent] est nul (répertoires de niveau 1 ou si on cherche à déplacer vers un sous-répertoire
-    if ( isset($_REQUEST['parent']) && isset($parents[$parent=$_REQUEST['parent']]) && ( $parent != $rep['parent'] ) && ( $rep['parent'] ) && ( !in_array($rid,explode(',',$parents[$parent])) ) )  {
-      // Récupération de l'éventuelle nouvelle matière
-      $resultat = $mysqli->query("SELECT matiere FROM reps WHERE id = $parent");
-      $r = $resultat->fetch_assoc();
-      $resultat->free();
-      $mat = $r['matiere'];
-      $parents = "${parents[$parent]},$parent";
-      // Sauvegarde de la table contenant les données
-      sauvegarde_mysql('docs');
-      // Modifications
-      $message .= ( $mysqli->query("UPDATE reps SET matiere = $mat, parent = $parent, parents = '$parents' WHERE id = $rid")
-                 && $mysqli->query('ALTER TABLE reps ORDER BY parents,nom')
-                 && $mysqli->query("UPDATE reps SET nbrep = (nbrep + 1) WHERE id = $parent")
-                 && $mysqli->query("UPDATE reps SET nbrep = (nbrep - 1) WHERE id = ${rep['parent']}")
-                 && $mysqli->query("UPDATE reps SET matiere = $mat, parents = '$parents,$rid' WHERE parent = $rid")
-                 && $mysqli->query("UPDATE docs SET matiere = $mat, parents = '$parents,$rid' WHERE parent = $rid")
-      ) ? ' Le répertoire a bien été déplacé.' : ' Le répertoire n\'a pas pu être déplacé. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'».';
-    }
-
-    // Traitement de la protection par mot de passe du répertoire
-    $protection = isset($_REQUEST['protection']) ? 1 : 0;
-    if ( $protection != $rep['protection'] )  {
-      // Sauvegarde de la table contenant les données
-      sauvegarde_mysql('docs');
-      $message .=  ( $mysqli->query("UPDATE reps SET protection = $protection WHERE id = $rid OR FIND_IN_SET($rid,parents)")
-                  && $mysqli->query("UPDATE docs SET protection = $protection WHERE FIND_IN_SET($rid,parents) AND protection < 2")
-      ) ? ' La protection par mot de passe du répertoire ainsi que tout ce qu\'il contient (sous-répertoires et fichiers) a bien été modifiée.' : ' La protection par mot de passe du répertoire n\'a pas pu être modifiée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'».';
-    }
-    
-    if ( !strlen($message) )
-      unset($message);
-  }
-
-  // Traiment d'une suppression d'un répertoire
-  elseif ( isset($_REQUEST['supprime']) && ( isset($parents[$id=$_REQUEST['id']]) ) && ( $parents[$id] != '0' ) )  {
-    // Récupération du nom du répertoire
-    $resultat = $mysqli->query("SELECT nom, parent FROM reps WHERE id = $id");
-    $r = $resultat->fetch_assoc();
-    $resultat->free();
-    // Demande de confirmation
-    if ( !isset($_REQUEST['confirme']) )  {
-      $message = "
-    <form action=\"\" method=\"post\">
-      <p>Souhaitez-vous réellement supprimer le répertoire «&nbsp;${r['nom']}&nbsp;» et tout son contenu&nbsp;? Les documents concernés seront effacés définitivement.</p>
-      <input type=\"submit\" name=\"confirme\" value=\"Oui\">
-      <input type=\"hidden\" name=\"supprime\" value=\"Supprimer\">
-      <input type=\"hidden\" name=\"id\" value=\"$id\">
-    </form>\n";
-    }
-    else  {
-      // Sauvegarde des tables contenant les données
-      sauvegarde_mysql('reps');
-      sauvegarde_mysql('docs');
-      // Liste des fichiers à supprimer
-      $resultat = $mysqli->query("SELECT GROUP_CONCAT(CONCAT('documents/',lien) SEPARATOR ' ') FROM docs WHERE FIND_IN_SET($id,parents)");
-      $s = $resultat->fetch_row();
-      $resultat->free();
-      if ( $mysqli->query("DELETE FROM reps WHERE id = $id OR FIND_IN_SET($id,parents)")
-        && $mysqli->query("DELETE FROM docs WHERE FIND_IN_SET($id,parents)")
-        && $mysqli->query("UPDATE reps SET nbrep = (nbrep - 1) WHERE id = ${r['parent']}") )  {
-        $message = "La suppression du répertoire «&nbsp;${r['nom']}&nbsp;» et de tout ce qu'il contient (sous-répertoires et fichiers) a bien été effectuée.";
-        // Suppression physique
-        if ( strlen($s[0]) )
-          exec("rm -rf ${s[0]}");
-        // Mise à jour des données : afficher le répertoire parent si on a supprimé le répertoire à afficher
-        if ( $id == $rid )
-          $rid = $rep['parent'];
-      }
-      else
-        $message = "La suppression du répertoire «&nbsp;${r['nom']}&nbsp;» n'a pas pu être effectuée. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'».';
-    }
-  }
-
-  // Traiment d'une création d'un sous-répertoire
-  elseif ( isset($_REQUEST['cree']) )  {
-    $nom = $mysqli->real_escape_string($_REQUEST['nom']);
-    $protection = isset($_REQUEST['protection']) ? 1 : 0;
-    // Sauvegarde de la table contenant les données
-    sauvegarde_mysql('reps');
-    $message = ( $mysqli->query("INSERT INTO reps SET parent = $rid, parents = '${parents[$rid]},$rid', nom = '$nom', matiere = (SELECT r.matiere FROM reps AS r WHERE r.id = $rid), nbrep = 0, nbfic = 0, protection = $protection")
-              && $mysqli->query("UPDATE reps SET nbrep = (nbrep + 1) WHERE id = $rid")
-              && $mysqli->query('ALTER TABLE reps ORDER BY parents,nom')
-    ) ? 'Le répertoire «&nbsp;'.stripslashes($nom).'&nbsp;» a bien été créé.' : 'Le répertoire «&nbsp;'.stripslashes($nom).'&nbsp;» n\'a pas pu être créé. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'».';
-  }
-
-  // Traitement d'un envoi de fichier
-  elseif ( isset($_REQUEST['envoie']) )  {
-    // Vérifications des données envoyées (on fait confiance aux utilisateurs connectés pour ne pas envoyer de scripts malsains)
-    $nom = $_FILES['fichier']['name'];
-    $ext = ( strpos($nom,'.') ) ? strrchr($nom,'.') : '';
-    $nom = ( strlen($_REQUEST['nom']) ) ? $_REQUEST['nom'] : $nom;
-    $nom = preg_replace('#[\x00-\x1F\x7F-\x9F/\\\\]#','',basename(str_replace($ext,'',$nom)));
-    $protection = ( in_array($_REQUEST['protection'],array(0,1,2)) ) ? $_REQUEST['protection'] : 0;
-    $lien = substr(sha1(mt_rand()),0,15);
-    // Déplacement du document uploadé au bon endroit
-    if ( mkdir("documents/$lien") && move_uploaded_file($_FILES['fichier']['tmp_name'],"documents/$lien/$nom$ext") )  {
-      // Gestion de la taille
-      $taille = intval($_FILES['fichier']['size']/1024);
-      $taille = ( $taille < 1024 ) ? "$taille&nbsp;ko" : intval($taille/1024).'&nbsp;Mo';
-      // Écriture MySQL
-      if ( $mysqli->query("INSERT INTO docs SET parent = $rid, parents = '${parents[$rid]},$rid',
-                           matiere = (SELECT matiere FROM reps WHERE id = $rid), nom = '".$mysqli->real_escape_string($nom)."',
-                           upload = DATE(NOW()), taille = '$taille', lien = '$lien', ext='$ext', protection = $protection") )  {
-        $id = $mysqli->insert_id;
-        $mysqli->query('ALTER TABLE docs ORDER BY parents,nom');
-        $message = "Le document «&nbsp;$nom$ext&nbsp;» a bien été mis en ligne.";
-        // Si le fichier n'est pas caché : mise à jour du nb de fichiers, information en page d'accueil
-        if ( $protection < 2 )  {
-          $mysqli->query("UPDATE reps SET nbfic = (nbfic + 1) WHERE id = $rid");
-          // Sauvegarde de la table contenant les données
-          sauvegarde_mysql('infos');
-          $message .= ( $mysqli->query('UPDATE infos SET ordre = (ordre+1) WHERE page = 0') 
-                     && $mysqli->query("INSERT INTO infos SET ordre = 1, page = 0, auto = 'doc-$id', cache = 0, texte = '<p>Nouveau document&nbsp;: «&nbsp;<a href=\"download?id=$id\">".$mysqli->real_escape_string($nom)."</a>&nbsp;» dans le répertoire <a href=\"docs?rep=$rid\">".addslashes($rep['nom']).'</a>.</p>\', titre = \''.date('d/m/y').'&nbsp;: nouveau document à télécharger\'')
-                     && $mysqli->query('ALTER TABLE infos ORDER BY ordre,page')
-          ) ? "<br>Un lien vers ce document a été ajouté en haut de la page d'accueil. Ce lien est <a href=\".?accueil$urladmin\">modifiable/supprimable.</a>." : '';
-        }
-      }
-      else  {
-        // Retour en arrière
-        exec("rm -rf documents/$lien");
-        $message = "Le document «&nbsp;$nom$ext&nbsp;» n'a pas été mis en ligne à cause d'une erreur lors des modifications dans la base de données MySQL. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'».';
-      }
-    }
-    else
-      $message = "Le document «&nbsp;$nom$ext&nbsp;» n'a pas été mis en ligne car le fichier envoyé n'est pas valide.";
-  }
-
-  // Traitement d'une modification d'un fichier
-  elseif ( isset($_REQUEST['modifie_fichier']) && is_numeric($id = $_REQUEST['id']) )  {
-    // Récupération des données relatives au fichier
-    $resultat = $mysqli->query("SELECT nom, protection, parent, ext, lien FROM docs WHERE id = $id");
-    if ( $resultat->num_rows )  {
-      $r = $resultat->fetch_assoc();
-      $resultat->free();
-      if ( !strlen($_REQUEST['nom']) )
-        $message = 'Il n\'est pas possible de valider un nom de fichier vide. Pour supprimer un fichier, il faut cliquer sur Supprimer.';
-      else  {
-        $message = '';
-        // Sauvegarde de la table contenant les données
-        sauvegarde_mysql('docs');
-        // Validation des données envoyées (real_escape_string à appliquer seulement dans les requêtes !)
-        $nom = preg_replace('#[\x00-\x1F\x7F-\x9F/\\\\]#','',basename(str_replace($r['ext'],'',$_REQUEST['nom'])));
-        $protection = ( in_array($_REQUEST['protection'],array(0,1,2)) ) ? $_REQUEST['protection'] : 0;
-
-        // Modification du nom
-        if ( $nom != $r['nom'] )  {
-          setlocale(LC_CTYPE, "fr_FR.UTF-8");
-          if ( $mysqli->query('UPDATE docs SET nom = \''.$mysqli->real_escape_string($nom)."' WHERE id = $id") )  {
-            exec('mv documents/'.escapeshellarg("${r['lien']}/${r['nom']}${r['ext']}").' documents/'.escapeshellarg("${r['lien']}/$nom${r['ext']}"));
-            $mysqli->query('ALTER TABLE docs ORDER BY parents,nom');
-            $message = " Le nom du fichier «&nbsp;${r['nom']}${r['ext']}&nbsp;» a bien été modifié.";
-          }
-          else
-            $message ="Le nom du fichier «&nbsp;${r['nom']}${r['ext']}&nbsp;» n'a pas pu être modifié. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'».';
-        }
-
-        // Modification de la protection
-        if ( $protection != $r['protection'] )  {
-          if ( $mysqli->query("UPDATE docs SET protection = $protection WHERE id = $id") )  {
-            $message .= " La protection par mot de passe du fichier «&nbsp;${r['nom']}${r['ext']}&nbsp;» a bien été modifiée.";
-            // Si une des deux valeurs vaut 2, le fichier apparaît ou disparaît du listing public 
-            if ( $protection + $r['protection'] > 1 )  {
-              // Sauvegarde de la table contenant les données
-              sauvegarde_mysql('reps');
-              if ( $protection == 2 )  {
-                // Le fichier disparaît du décompte du répertoire et des informations de la page d'accueil
-                $mysqli->query("UPDATE reps SET nbfic = (nbfic - 1) WHERE id = ${r['parent']}");
-                $resultat = $mysqli->query("SELECT id, ordre FROM infos WHERE auto = 'doc-$id'");
-                if ( $resultat->num_rows )  {
-                  $s = $resultat->fetch_assoc();
-                  $resultat->free();
-                  $message .= ( $mysqli->query("DELETE FROM infos WHERE id = ${s['id']}")
-                             && $mysqli->query("UPDATE infos SET ordre = (ordre-1) WHERE ordre > ${s['ordre']} AND page = 0")
-                  ) ? '<br>Le lien existant depuis la page d\'accueil a été automatiquement supprimé.' : '';
-                }
-              }
-              else {
-                // Le fichier réapparaît du décompte du répertoire et des informations de la page d'accueil
-                $mysqli->query("UPDATE reps SET nbfic = (nbfic + 1) WHERE id = ${r['parent']}");
-                $message .= ( $mysqli->query('UPDATE infos SET ordre = (ordre+1) WHERE page = 0') 
-                           && $mysqli->query("INSERT INTO infos SET ordre = 1, page = 0, auto = 'doc-$id', cache = 0, texte = CONCAT('<p>Nouveau document&nbsp;: «&nbsp;<a href=\"download?id=$id\">".$mysqli->real_escape_string($nom)."</a>&nbsp;» dans le répertoire <a href=\"docs?rep=${r['parent']}\">',(SELECT reps.nom FROM reps WHERE reps.id = ${r['parent']}),'</a>.</p>'), titre = '".date('d/m/y').'&nbsp;: nouveau document à télécharger\'')
-                           && $mysqli->query('ALTER TABLE infos ORDER BY ordre,page')
-                ) ? "<br>Un lien vers ce document a été ajouté en haut de la page d\'accueil. Ce lien est <a href=\".?accueil$urladmin\">modifiable/supprimable.</a>." : '';
-              }
-            }
-          }
-          else
-            $message .= " La protection par mot de passe du fichier «&nbsp;${r['nom']}${r['ext']}&nbsp;» n'a pas pu être modifiée. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'».';
-        }
-        
-        // Déplacement d'un répertoire à un répertoire
-        if ( isset($parents[$parent = $_REQUEST['parent']]) && ( $parent != $r['parent'] ) )  {
-          // Sauvegarde de la table contenant les données
-          sauvegarde_mysql('reps');
-          if ( $mysqli->query("UPDATE docs SET parent = '$parent', parents = '${parents[$parent]},$parent',
-                               matiere = (SELECT matiere FROM reps WHERE id = $parent) WHERE id = $id") )  {
-            if ( $protection < 2 )  {
-              $mysqli->query("UPDATE reps SET nbfic = (nbfic - 1) WHERE id = ${r['parent']}");
-              $mysqli->query("UPDATE reps SET nbfic = (nbfic + 1) WHERE id = $parent");
-            }
-            $mysqli->query('ALTER TABLE docs ORDER BY parents,nom');
-            $message .= " Le fichier «&nbsp;${r['nom']}${r['ext']}&nbsp;» a bien été déplacé.";
-          }
-          else
-            $message .= " Le fichier «&nbsp;${r['nom']}${r['ext']}&nbsp;» n'a pas pu être déplacé. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'».';
-        }
-        
-        if ( !strlen($message) )
-          unset($message);
-      }
-    }
-  }
-  
-  // Traitement d'une suppression d'un fichier
-  elseif ( isset($_REQUEST['supprime_fichier']) && is_numeric($id = $_REQUEST['id']) )  {
-    // Récupération des données relatives au fichier
-    $resultat = $mysqli->query("SELECT nom, parent, lien, ext, protection FROM docs WHERE id = $id");
-    if ( $resultat->num_rows )  {
-      $r = $resultat->fetch_assoc();
-      $resultat->free();
-      // Sauvegarde de la table contenant les données
-      sauvegarde_mysql('docs');
-      if ( $mysqli->query("DELETE FROM docs WHERE id = $id") )  {
-        $message = "Le fichier «&nbsp;${r['nom']}${r['ext']}&nbsp;» a bien été supprimé.";
-        // Suppression physique
-        exec("rm -rf documents/${r['lien']}");
-        if ( $r['protection'] < 2 )  {
-          // Sauvegarde de la table contenant les données
-          sauvegarde_mysql('reps');
-          $mysqli->query("UPDATE reps SET nbfic = (nbfic - 1) WHERE id = ${r['parent']}");
-          // Suppression de l'éventuelle information en page d'accueil
-          $resultat = $mysqli->query("SELECT id, ordre FROM infos WHERE auto = 'doc-$id'");
-          if ( $resultat->num_rows )  {
-            $r = $resultat->fetch_assoc();
-            $resultat->free();
-            $message .= ( $mysqli->query("DELETE FROM infos WHERE id = ${r['id']}")
-                       && $mysqli->query("UPDATE infos SET ordre = (ordre-1) WHERE ordre > ${r['ordre']} AND page = 0")
-            ) ? '<br>Le lien existant depuis la page d\'accueil a été automatiquement supprimé.' : '';
-          }
-        }
-      }
-      else
-        $message = "Le fichier «&nbsp;${r['nom']}${r['ext']}&nbsp;» n'a pas pu être supprimé. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'».';
-    }
-  }
-
-  // Mise à jour des champs 'docs' dans la table 'matieres' (pour le menu)
-  $mysqli->query('UPDATE matieres SET docs = (SELECT IF(nbfic+nbrep,1,0) FROM reps WHERE parent=0 AND matiere = matieres.id)');
-    
-  // Passage en connexion MySQL en lecture seulement
-  $mysqli->close();
-  $mysqli = new mysqli($serveur,$base,$mdp,$base);
-  $mysqli->set_charset('utf8');
-}
-
-//////////
-// HTML //
-//////////
-
-// Récupération des données du répertoire
-$resultat = $mysqli->query("SELECT r.id, r.parent, r.parents, r.nom, r.protection, m.cle
-                            FROM reps AS r LEFT JOIN matieres AS m ON r.matiere = m.id
-                            WHERE r.id = $rid");
-$rep = $resultat->fetch_assoc();
-$resultat->free();
-
-// Récupération des répertoires, dans le bon ordre pour un select
-function liste($rid,$n)  {
-  $resultat = $GLOBALS['mysqli']->query("SELECT id, nom, parents, IF(FIND_IN_SET(".$GLOBALS['rid'].",parents),' disabled','') AS disabled
-                                         FROM reps WHERE parent = $rid");
-  while ( $r = $resultat->fetch_assoc() )  {
-    $GLOBALS['select_reps'] .= "\n        <option value=\"${r['id']}\"${r['disabled']}>".str_repeat('&rarr;',$n)."${r['nom']}</option>";
-    liste($r['id'],$n+1);
-  }
-  $resultat->free();
-}
-$select_reps = '';
-liste(0,0);
-
-// Recherche et affichage récursif
-$select = str_replace(' disabled','',$select_reps);
-function affichage($rid, $n)  {
-  $indent = str_pad('',2*$n);
-  // Pour les form, préciser le rep dans action
-  $lien = "?rep=${GLOBALS['rid']}";
-  
-  // Récupération des données du répertoire
-  $resultat = $GLOBALS['mysqli']->query("SELECT nom, parent, IF(protection,'-lock','') AS protection, IF(nbrep+nbfic=0,' (répertoire vide)','') AS vide
-                                         FROM reps WHERE id = $rid");
-  $r = $resultat->fetch_assoc();
-  $resultat->free();
-
-  // Affichage du répertoire considéré
-  $suppr = ( $r['parent'] ) ? "\n$indent    <input type=\"submit\" name=\"supprime\" value=\"Supprimer\" title=\"Supprimer ce répertoire\">" : '';
-  $protection = ( $r['protection'] ) ? ' lock' : '';
-  echo "
-$indent<div class=\"rep\">
-$indent  <form action=\"$lien\" method=\"post\"><p class=\"rep open$protection\"><img class=\"icone\" src=\"icones/rep-open${r['protection']}.png\">$suppr
-$indent    <input type=\"hidden\" name=\"id\" value=\"$rid\">
-$indent    <a href=\"?rep=$rid\">${r['nom']}</a>${r['vide']}</p></form>";
-
-  // Recherche de sous-répertoire et récursivité
-  $resultat = $GLOBALS['mysqli']->query("SELECT id FROM reps WHERE parent = $rid");
-  if ( $resultat->num_rows )  {
-    while ( $s = $resultat->fetch_assoc() )
-      affichage($s['id'],$n+1);
-    $resultat->free();
-  }
-  // Fichiers
-  $resultat = $GLOBALS['mysqli']->query("SELECT id, nom, lien, ext, protection FROM docs WHERE parent = $rid");
-  if ( $resultat->num_rows )  {
-    while ( $s = $resultat->fetch_assoc() )  {
-      switch ( $s['ext'] )  {
-        case '.pdf':
-          $icone = 'pdf';
-          break;
-        case '.doc':
-        case '.odt':
-        case '.docx':
-          $icone = 'doc';
-          break;
-        case '.xls':
-        case '.ods':
-        case '.xlsx':
-          $icone = 'xls';
-          break;
-        case '.ppt':
-        case '.odp':
-        case '.pptx':
-          $icone = 'ppt';
-          break;
-        case '.jpg':
-        case '.jpeg':
-        case '.png':
-        case '.gif':
-        case '.svg':
-        case '.tif':
-        case '.tiff':
-        case '.bmp':
-        case '.ps':
-        case '.eps':
-          $icone = 'jpg';
-          break;
-        case '.avi':
-        case '.mpeg':
-        case '.mpg':
-        case '.wmv':
-        case '.mp4':
-        case '.ogv':
-        case '.qt':
-        case '.mov':
-        case '.mkv':
-          $icone = 'avi';
-          break;
-        case '.mp3':
-        case '.ogg':
-        case '.oga':
-        case '.wma':
-        case '.wav':
-        case '.ra':
-        case '.rm':
-          $icone = 'mp3';
-          break;
-        case '.txt':
-        case '.rtf':
-          $icone = 'txt';
-          break;
-        case '.zip':
-        case '.rar':
-        case '.7z':
-          $icone = 'zip';
-          break;
-        case '.exe':
-        case '.sh':
-        case '':
-          $icone = 'exe';
-          break;
-        default :
-          $icone = 'defaut';
-      }
-      switch ( $s['protection'] )  {
-        case 1:
-          $icone .= '-lock';
-        case 0:
-          $protection = '';
-          break;
-        case 2:
-          $protection = ' (non visible)';
-      }
-      $select = str_replace(array("\"$rid\"",'      '),array("\"$rid\" selected","$indent      "),$GLOBALS['select']);
-      $select_protection = str_replace("\"${s['protection']}\"","\"${s['protection']}\" selected",'<option value="0">Visible de tous</option><option value="1">Avec mot de passe</option><option value="2">Caché</option>');
-      echo "
-$indent  <div class=\"fic\">
-$indent    <span><img class=\"icone\" src=\"icones/$icone.png\">${s['nom']}</span>$protection
-$indent    <form action=\"$lien\" method=\"post\">
-$indent      <p class=\"boutons\"><input type=\"submit\" name=\"modifie_fichier\" value=\"Valider\"><input type=\"submit\" name=\"supprime_fichier\" value=\"Supprimer\" title=\"Supprimer ce fichier\"></p>
-$indent      <p class=\"ligne\"><label for=\"nom\">Renommer&nbsp;: </label><input type=\"text\" id=\"nom\" name=\"nom\" value=\"${s['nom']}\" size=\"50\"></p>
-$indent      <p class=\"ligne\"><label for=\"parent\">Déplacer&nbsp;: </label><select id=\"parent\" name=\"parent\">$select
-$indent      </select></p>
-$indent      <p class=\"ligne\"><label for=\"protection\">Accès&nbsp;: </label><select id=\"protection\" name=\"protection\">
-$indent        $select_protection
-$indent      </select></p>
-$indent      <input type=\"hidden\" name=\"id\" value=\"${s['id']}\">
-$indent      <p class=\"ligne\"><label>Lien&nbsp;: </label><code>&lt;a href=\"download?id=${s['id']}\"&gt;${s['nom']}&lt;/a&gt;</code></p>
-$indent    </form>
-$indent  </div>";
-    }
-    $resultat->free();
-  }
-  echo "
-$indent</div>";
-}
-
-// Haut de page, menu et message
-$p = ( is_null($rep['cle']) ) ? 'docs_admin' : "docs_admin?${rep['cle']}";
-$t = "Gestion des répertoires et documents - «&nbsp;${rep['nom']}&nbsp;»";
-include('haut.php');
-
-// Répertoires parents
-$resultat = $mysqli->query("SELECT id, nom, IF(protection,'-lock','') AS protection FROM reps WHERE FIND_IN_SET(id,'${rep['parents']}')");
-if ( $resultat->num_rows )  {
-  $icone = 'home';
-  echo "\n  <div class=\"item\" id=\"parents\">";
-  while ( $r = $resultat->fetch_assoc() )  {
-    echo "\n    <a href=\"?rep=${r['id']}\"><img class=\"icone\" src=\"icones/$icone${r['protection']}.png\">${r['nom']}</a>";
-    $icone = 'rep-open';
-  }
-  echo "\n  </div>\n";
-  $resultat->free();
-}
-else
-  echo "\n  <div id=\"parents-vide\"> </div>\n";
-
-// Affichage du répertoire et de son contenu
-affichage($rid,1);
-$mysqli->close();
-
-// Formulaires :
-// * modification du répertoire
-// * envoi de fichier
-// * création de sous-répertoire
-// Vérification de la quantité de données téléchargeable
-$taille = min(ini_get('upload_max_filesize'),ini_get('post_max_size'));
-if ( stristr($taille,'m') )
-  $taille = substr($taille,0,-1)*1048576;
-elseif ( stristr($taille,'k') )
-  $taille = substr($taille,0,-1)*1024;
-$message = ( $taille < 1048576 ) ? intval($taille/1024).'&nbsp;ko.' : intval($taille/1048576).'&nbsp;Mo.';
-if ( $taille < 2097152 )
-  $message .= ' Cela pourrait être insuffisant si vous souhaitez envoyer des documents conséquents, mais n\'interdit pas l\'envoi de petits documents.';
-// Protection par défaut pour les nouveaux répertoires/fichiers si le répertoire est protégé
-$protection = ( $rep['protection'] ) ? ' checked' : '';
-$select_protection = str_replace("\"${rep['protection']}\"","\"${rep['protection']}\" selected",'<option value="0">Visible de tous</option><option value="1">Avec mot de passe</option><option value="2">Caché</option>');
-// Pour le déplacement : sélection du bon répertoire ; ne pas déplacer les répertoires de niveau 1 (Général et matières)
-$select_reps = str_replace(array("\"$rid\"","\"${rep['parent']}\""),array("\"$rid\" disabled","\"${rep['parent']}\" selected"),$select_reps);
-$select_reps = ( $rep['parent'] ) ? '<select id="parent" name="parent">'.$select_reps : '<select id="parent" name="parent" disabled>'.$select_reps;
-?>
-
-  <div class="item aide">
-    <p>Vous pouvez ici modifier les répertoires et les documents contenus. Les répertoires s'ouvrent en cliquant sur leur icone, les propriétés modifiables des fichiers s'affichent lorsque l'on clique sur leur nom.</p>
-    <p>Un fichier peut être caché de la partie publique&nbsp;: il n'apparaît qu'ici, il est strictement indisponible au téléchargement. Cela peut être intéressant pour s'avancer (un sujet de devoir ou un corrigé par exemple).</p>
-    <p>Après avoir cliqué sur un répertoire, vous pouvez ci-dessous modifier son nom ou son accès, y ajouter un sous-répertoire ou un fichier. Un répertoire dont l'<em>Accès avec mot de passe</em> est activé ne pourra être vu uniquement par les utilisateurs ayant entré un mot de passe. Les utilisateurs sont modifiables <a href"utilisateurs">ici</a>.</p>
-    <p>En changeant l'accès avec mot de passe ou non d'un répertoire, tous les sous-répertoires et fichiers contenus auront automatiquement la même protection, excepté les fichiers cachés (qui restent toujours cachés).</p>
-    <p>Lorsque vous envoyez un fichier, son nom est automatiquement récupéré, sauf si vous remplissez la case <em>Nom à afficher</em>. La taille d'un fichier est limitée à <?php echo $message; ?></p>
-    <p>Les liens vers les fichiers et les répertoires ne sont jamais modifiés par un déplacement ou un changement de nom.</p>
-    <p>À chaque nouveau fichier envoyé et non caché (ou lorsqu'un fichier passe de «&nbsp;caché&nbsp;» à «&nbsp;montré&nbsp;»), une information est automatiquement écrite en haut de la page d'accueil. Elle est <a href="?accueil<?php echo $urladmin; ?>">modifiable</a>.</p>
-  </div>
-
-  <div class="item admin">
-  <form action="<?php echo "?rep=$rid"; ?>" method="post">
-    <input class="bouton" type="submit" name="modifie" value="Valider" title="Valider les modifications">
-<!--    <input class="bouton" type="submit" name="supprime" value="Supprimer" title="Supprimer ce répertoire et son contenu">-->
-    <h3>Modification du répertoire</h3>
-    <p class="ligne"><label for="nom">Nom&nbsp;: </label><input type="text" id="nom" name="nom" value="<?php echo $rep['nom']; ?>" size="50"></p>
-    <p class="ligne"><label for="parent">Déplacer&nbsp;: </label>
-      <?php echo $select_reps; ?>
-
-      </select>
-    </p>
-    <p class="ligne"><label for="protection">Accès avec mot de passe&nbsp;: </label><input type="checkbox" id="protection" name="protection" value="1"<?php echo $protection; ?>></p>
-  </form>
-  </div>
-
-  <div class="item admin">
-  <form action="<?php echo "?rep=$rid"; ?>" method="post" enctype="multipart/form-data">
-    <input class="bouton" type="submit" name="envoie" value="Envoyer">
-    <h3>Déposer un fichier</h3>
-    <p class="ligne"><label for="nom">Nom à afficher&nbsp;: </label><input type="text" id="nom" name="nom" value="" size="50"></p>
-    <p class="ligne"><label for="fichier">Fichier&nbsp;: </label><input type="file" name="fichier"></p>
-    <p class="ligne"><label for="protection">Accès&nbsp;: </label>
-      <select id="protection" name="protection"><?php echo $select_protection; ?></select>
-    </p>
-  </form>
-  </div>
-
-  <div class="item admin">
-  <form action="<?php echo "?rep=$rid"; ?>" method="post" enctype="multipart/form-data">
-    <input class="bouton" type="submit" name="cree" value="Créer">
-    <h3>Créer un sous-répertoire</h3>
-    <p class="ligne"><label for="nom">Nom&nbsp;: </label><input type="text" id="nom" name="nom" value="" size="50"></p>
-    <p class="ligne"><label for="protection">Accès avec mot de passe&nbsp;: </label><input type="checkbox" id="protection" name="protection" value="1"<?php echo $protection; ?>></p>
-  </form>
-  </div>
-
-  <script type="text/javascript">
-$( function() {
-  $('p.rep img').css('cursor','pointer').click( function () {
-    var p = $(this).parent();
-    p.toggleClass('open');
-    if ( p.is('.open') )  {
-      $(this).attr('src',p.is('.lock') ? 'icones/rep-open-lock.png' : 'icones/rep-open.png');
-      p.parent().parent().children('.fic,div.rep').css('display','block');
-    }
-    else  {
-      $(this).attr('src',p.is('.lock') ? 'icones/rep-lock.png' : 'icones/rep.png');
-      p.parent().parent().children('.fic,div.rep').css('display','none');
-    }
-  });
-
-  $('#contenu > div.rep > div.rep').find('p.rep:not(.lock) img').attr('src','icones/rep.png').parent().removeClass('open');
-  $('#contenu > div.rep > div.rep').find('p.rep.lock img').attr('src','icones/rep-lock.png').parent().removeClass('open');
-  $('#contenu > div.rep > div.rep').find('.fic,div.rep').css('display','none');
-  $('.fic').find('form').hide();
-  $('.fic span').replaceWith(function() { return '<a href="">'+$(this).html()+'</a>'; });
-  $('.fic a').click( function () {
-    $(this).parent().find('form').toggle();
-    $(this).parent().toggleClass('admin');
-    return false;
-  });
-});
-  </script>
-
-<?php
-// Bas de page
-include('bas.php');
-?>
diff -urN cahier-de-prepa2.2.0/docs.php cahier-de-prepa3.0.0/docs.php
--- cahier-de-prepa2.2.0/docs.php	2013-01-03 00:00:09.000000000 +0100
+++ cahier-de-prepa3.0.0/docs.php	2013-08-24 01:03:17.073799841 +0200
@@ -3,122 +3,19 @@
 define('OK',1);
 include('debut.php');
 
-//////////
-// HTML //
-//////////
-
-// Recherche et affichage récursif
-function affichage($r, $n)  {
-  $indent = str_pad('',2*$n);
-
-  // Affichage du répertoire considéré
-  $indication = array();
-  if ( $r['nbrep'] )
-    $indication[] = ( $r['nbrep'] > 1 ) ? "${r['nbrep']} répertoires" : "${r['nbrep']} répertoire";
-  if ( $r['nbfic'] )
-    $indication[] = ( $r['nbfic'] > 1 ) ? "${r['nbfic']} fichiers" : "${r['nbfic']} fichier";
-  $indication = ( empty($indication) ) ? '' : ' ('.implode(', ',$indication).')';
-  echo "
-$indent<div class=\"rep\">
-$indent  <p class=\"rep ${r['protection']}\"><img class=\"icone\" src=\"icones/rep-${r['protection']}.png\"><a href=\"?rep=${r['id']}\">${r['nom']}</a>$indication</p>";
-
-  // Pas de protection sur ce répertoire, affichage de son contenu
-  if ( $r['protection'] != 'lock' )  {
-    $protection = ( !$GLOBALS['lecteur'] ) ? 'IF(protection,\'lock\',\'open\')' : '\'open\'';
-    // Sous-répertoires et récursivité
-    $resultat = $GLOBALS['mysqli']->query("SELECT id, nom, nbfic, nbrep, $protection AS protection FROM reps WHERE parent = ${r['id']}");
-    if ( $resultat->num_rows )  {
-      while ( $s = $resultat->fetch_assoc() )
-        affichage($s,$n+1);
-      $resultat->free();
-    }
-    // Fichiers
-    $protection = ( !$GLOBALS['lecteur'] ) ? 'IF(protection,\'-lock\',\'\')' : '\'\'';
-    $resultat = $GLOBALS['mysqli']->query("SELECT id, nom, taille, DATE_FORMAT(upload,'%d/%m/%Y') AS upload, ext, $protection AS protection FROM docs WHERE parent = ${r['id']} AND protection < 2");
-    if ( $resultat->num_rows )  {
-      while ( $s = $resultat->fetch_assoc() )  {
-        switch ( $s['ext'] )  {
-          case '.pdf':
-            $icone = 'pdf';
-            break;
-          case '.doc':
-          case '.odt':
-          case '.docx':
-            $icone = 'doc';
-            break;
-          case '.xls':
-          case '.ods':
-          case '.xlsx':
-            $icone = 'xls';
-            break;
-          case '.ppt':
-          case '.odp':
-          case '.pptx':
-            $icone = 'ppt';
-            break;
-          case '.jpg':
-          case '.jpeg':
-          case '.png':
-          case '.gif':
-          case '.svg':
-          case '.tif':
-          case '.tiff':
-          case '.bmp':
-          case '.ps':
-          case '.eps':
-            $icone = 'jpg';
-            break;
-          case '.avi':
-          case '.mpeg':
-          case '.mpg':
-          case '.wmv':
-          case '.mp4':
-          case '.ogv':
-          case '.qt':
-          case '.mov':
-          case '.mkv':
-            $icone = 'avi';
-            break;
-          case '.mp3':
-          case '.ogg':
-          case '.oga':
-          case '.wma':
-          case '.wav':
-          case '.ra':
-          case '.rm':
-            $icone = 'mp3';
-            break;
-          case '.txt':
-          case '.rtf':
-            $icone = 'txt';
-            break;
-          case '.zip':
-          case '.rar':
-          case '.7z':
-            $icone = 'zip';
-            break;
-          case '.exe':
-          case '.sh':
-          case '':
-            $icone = 'exe';
-            break;
-          default :
-            $icone = 'defaut';
-        }
-        echo "
-$indent  <p class=\"fic\"><a href=\"download?id=${s['id']}\"><img class=\"icone\" src=\"icones/$icone${s['protection']}.png\">${s['nom']}</a> (${s['upload']}, ${s['taille']})</p>";
-      }
-      $resultat->free();
-    }
-  }
-  echo "
-$indent</div>";
-}
+///////////////////////////////////////////////
+// Validation de la requête : rep ou matière //
+///////////////////////////////////////////////
 
 // Récupération de l'identifiant de répertoire demandé
 $mysqli = premiere_connexion();
-if ( isset($_REQUEST['rep']) && is_numeric($_REQUEST['rep']) )
-  $rep = $_REQUEST['rep'];
+if ( isset($_REQUEST['rep']) && is_numeric($rid = $_REQUEST['rep']) )  {
+  $resultat = $mysqli->query("SELECT id FROM reps WHERE id = $rid");
+  if ( $resultat->num_rows )
+    $resultat->free();
+  else
+    $rid = 1;
+}
 else  {
   // Si une matière est demandée
   if ( !empty($_REQUEST) )  {
@@ -126,88 +23,707 @@
     if ( $resultat->num_rows )  {
       while ( $r = $resultat->fetch_assoc() )
       if ( isset($_REQUEST[$r['cle']]) )  {
-        $rep = $r['id'];
+        $rid = $r['id'];
         break;
       }
       $resultat->free();
     }
   }
-  if ( !isset($rep) )
-    $rep = 1;
+  // Valeur par défaut
+  if ( !isset($rid) )
+    $rid = 1;
 }
 
-// Récupération des données, vérification de la protection, répertoires parents
-$resultat = $mysqli->query("SELECT r.id, r.parents, r.nom, r.nbfic, r.nbrep, r.protection, m.cle, m.nom AS mat
-                            FROM reps AS r LEFT JOIN matieres AS m ON r.matiere = m.id
-                            WHERE r.id = $rep");
-if ( $resultat->num_rows )  {
+//////////////////////////////////
+// Modifications de répertoires //
+//////////////////////////////////
+if ( ( $admin ) && ( isset($_REQUEST['modifie']) || isset($_REQUEST['supprime']) || isset($_REQUEST['cree']) ) )  {
+  // Connexion à la base de données
+  $mysqli->close();
+  $mysqli = new mysqli($serveur,"$base-adm",$mdp,$base);
+  $mysqli->set_charset('utf8');
+
+  // Récupération des données du répertoire
+  $resultat = $mysqli->query("SELECT nom, parent, protection, nbrep+nbfic AS nb FROM reps WHERE id = $rid");
   $rep = $resultat->fetch_assoc();
   $resultat->free();
 
-  $p = ( is_null($rep['cle']) ) ? 'docs' : "docs?${rep['cle']}";
-  $t = ( is_null($rep['cle']) ) ? 'Documents à télécharger' : "Documents à télécharger - ${rep['mat']}";
+  // Récupération des liens de parenté entre répertoires
+  $resultat = $mysqli->query("SELECT id, parents FROM reps");
+  while ( $r = $resultat->fetch_assoc() )
+    $parents[$r['id']] = $r['parents'];
+  $resultat->free();
 
-  // Répertoires parents
-  $resultat = $mysqli->query("SELECT id, nom FROM reps WHERE FIND_IN_SET(id,'${rep['parents']}')");
-  if ( $resultat->num_rows )  {
-    $icone = 'home';
-    $parents = "\n  <div class=\"item\" id=\"parents\">";
+  // Sauvegarde des tables contenant les données -- une fois pour toutes
+  sauvegarde_mysql('reps');
+  sauvegarde_mysql('docs');
+
+  // Traitement d'une modification du répertoire actuel
+  // Une partie est impossible si $rep['parent'] est nul (répertoires racines de matières)
+  if ( isset($_REQUEST['modifie']) )  {
+    $message = '';
+
+    // Modification du nom
+    if ( isset($_REQUEST['nom']) && ( $rep['parent'] ) && ( strlen($nom = $_REQUEST['nom']) ) && ( $rep['nom'] != $nom ) )  {
+      $nom = $mysqli->real_escape_string($_REQUEST['nom']);
+      $message = ( $mysqli->query("UPDATE reps SET nom = '$nom' WHERE id = $rid")
+                && $mysqli->query('ALTER TABLE reps ORDER BY parents,nom')
+      ) ? 'Le nom du répertoire <em>'.stripslashes($rep['nom'] = $nom).'</em> a bien été modifié.' : "Le nom du répertoire <em>${rep['nom']}</em> n'a pas pu être modifié. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'».';
+    }
+
+    // Déplacement du répertoire
+    if ( isset($_REQUEST['parent']) && ( $rep['parent'] ) && isset($parents[$parent=$_REQUEST['parent']]) && ( $parent != $rep['parent'] ) && ( !in_array($rid,explode(',',$parents[$parent])) ) )  {
+      // Récupération de l'éventuelle nouvelle matière
+      $resultat = $mysqli->query("SELECT matiere FROM reps WHERE id = $parent");
+      $r = $resultat->fetch_assoc();
+      $resultat->free();
+      $mat = $r['matiere'];
+      $parents = "${parents[$parent]},$parent";
+      // Modifications
+      $message .= ( $mysqli->query("UPDATE reps SET matiere = $mat, parent = $parent, parents = '$parents' WHERE id = $rid")
+                 && $mysqli->query('ALTER TABLE reps ORDER BY parents,nom')
+                 && $mysqli->query("UPDATE reps SET nbrep = (nbrep + 1) WHERE id = $parent")
+                 && $mysqli->query("UPDATE reps SET nbrep = (nbrep - 1) WHERE id = ${rep['parent']}")
+                 && $mysqli->query("UPDATE reps SET matiere = $mat, parents = '$parents,$rid' WHERE parent = $rid")
+                 && $mysqli->query("UPDATE docs SET matiere = $mat, parents = '$parents,$rid' WHERE parent = $rid")
+      ) ? " Le répertoire <em>${rep['nom']}</em> a bien été déplacé." : " Le répertoire <em>${rep['nom']}</em> n'a pas pu être déplacé. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'».';
+    }
+
+    // Modification de la demande d'identification pour accéder au répertoire
+    $protection = isset($_REQUEST['protection']) ? 1 : 0;
+    if ( $protection != $rep['protection'] )
+      $message .= ( $mysqli->query("UPDATE reps SET protection = $protection WHERE id = $rid") ) ? " La visibilité du répertoire <em>${rep['nom']}</em> a bien été modifiée." : " La visibilité du répertoire <em>${rep['nom']}</em> n'a pas pu être modifiée. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'».';
+    
+    // Propagation de demande d'identification aux sous-répertoires et documents
+    if ( isset($_REQUEST['propagation']) && ( $rep['nb'] ) )
+      $message .=  ( $mysqli->query("UPDATE reps SET protection = $protection WHERE FIND_IN_SET($rid,parents)")
+                  && $mysqli->query("UPDATE docs SET protection = $protection WHERE FIND_IN_SET($rid,parents) AND protection < 2")
+      ) ? " La visibilité du répertoire <em>${rep['nom']}</em> a bien été propagée à tout son contenu (sous-répertoires et documents)." : " La visibilité du répertoire <em>${rep['nom']}</em> a bien été propagée à tout son contenu (sous-répertoires et documents). Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'».';
+
+    if ( !strlen($message) )
+      unset($message);
+  }
+
+  // Suppression du répertoire actuel
+  elseif ( isset($_REQUEST['supprime']) && ( $rep['parent'] ) && ( !$rep['nb'] ) )  {
+    $message = ( $mysqli->query("DELETE FROM reps WHERE id = $id")
+               && $mysqli->query("UPDATE reps SET nbrep = (nbrep - 1) WHERE id = ${rep['parent']}")
+    ) ? "La suppression du répertoire <em>${rep['nom']}</em> a bien été effectuée." : "La suppression du répertoire <em>${rep['nom']}</em> n'a pas été effectuée.";
+    $rid = $rep['parent'];
+  }
+
+  // Création d'un sous-répertoire
+  elseif ( isset($_REQUEST['cree']) && ( strlen($_REQUEST['nom']) ) )  {
+    $nom = $mysqli->real_escape_string($_REQUEST['nom']);
+    $protection = isset($_REQUEST['protection']) ? 1 : 0;
+    $message = ( $mysqli->query("INSERT INTO reps SET parent = $rid, parents = '${parents[$rid]},$rid', nom = '$nom', matiere = (SELECT r.matiere FROM reps AS r WHERE r.id = $rid), nbrep = 0, nbfic = 0, protection = $protection")
+              && $mysqli->query("UPDATE reps SET nbrep = (nbrep + 1) WHERE id = $rid")
+              && $mysqli->query('ALTER TABLE reps ORDER BY parents,nom')
+    ) ? 'Le répertoire <em>'.stripslashes($nom).'</em> a bien été créé.' : 'Le répertoire <em>'.stripslashes($nom).'</em> n\'a pas pu être créé. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'».';
+  }
+
+  // Mise à jour des champs 'docs' dans la table 'matieres' (pour le menu)
+  $mysqli->query('UPDATE matieres SET docs = (SELECT IF(SUM(nbfic),1,0) FROM reps WHERE matiere = matieres.id)');
+  // Passage en connexion MySQL en lecture seulement
+  $mysqli->close();
+  $mysqli = new mysqli($serveur,$base,$mdp,$base);
+  $mysqli->set_charset('utf8');
+}
+
+////////////////////////////////
+// Modifications de documents //
+////////////////////////////////
+elseif ( ( $admin ) && isset($_REQUEST['id']) && is_numeric($id = $_REQUEST['id']) )  {
+  // Connexion à la base de données
+  $mysqli->close();
+  $mysqli = new mysqli($serveur,"$base-adm",$mdp,$base);
+  $mysqli->set_charset('utf8');
+
+  // Récupération des données relatives au document
+  if ( $id )  {
+    $resultat = $mysqli->query("SELECT nom, protection, parent, ext, lien FROM docs WHERE id = $id");
+    if ( $resultat->num_rows )  {
+      $doc = $resultat->fetch_assoc();
+      $resultat->free();
+    }
+    else
+      $id = 0;
+  }
+
+  // Sauvegarde des tables contenant les données -- une fois pour toutes
+  sauvegarde_mysql('reps');
+  sauvegarde_mysql('docs');
+
+  // Traitement d'une modification d'un document
+  if ( isset($_REQUEST['modifie_doc']) && ( $id ) )  {
+    if ( !strlen($_REQUEST['nom']) )
+      $message = 'Il n\'est pas possible de valider un nom de fichier vide. Pour supprimer un fichier, il faut cliquer sur Supprimer.';
+    else  {
+      $message = '';
+
+      // Modification du nom
+      setlocale(LC_CTYPE, "fr_FR.UTF-8");
+      $nom = basename(str_replace($doc['ext'],'',$_REQUEST['nom']));
+      if ( $nom != $doc['nom'] )  {
+        // real_escape_string seulement pour la requête SQL
+        if ( $mysqli->query('UPDATE docs SET nom = \''.$mysqli->real_escape_string($nom)."' WHERE id = $id") )  {
+          exec('mv documents/'.escapeshellarg("${doc['lien']}/${doc['nom']}${doc['ext']}").' documents/'.escapeshellarg("${doc['lien']}/$nom${doc['ext']}"));
+          $mysqli->query('ALTER TABLE docs ORDER BY parents,nom');
+          $message = " Le nom du document <em>$nom</em> a bien été modifié.";
+          $doc['nom'] = $nom;
+        }
+        else
+          $message ="Le nom du document <em>${doc['nom']}</em> n'a pas pu être modifié. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'».';
+      }
+
+      // Modification de l'accès
+      // Si $protection = 2, on enlève un document du répertoire parent ; sinon on ajoute
+      $protection = ( in_array($_REQUEST['protection'],array(0,1,2)) ) ? $_REQUEST['protection'] : 0;
+      if ( $protection != $doc['protection'] )
+        $message .= ( $mysqli->query("UPDATE docs SET protection = $protection WHERE id = $id")
+                   && $mysqli->query("UPDATE reps SET nbfic = (nbfic + FLOOR(${doc['protection']}/2) - FLOOR($protection/2)) WHERE id = ${doc['parent']}") 
+        ) ? " L'accès au document <em>${doc['nom']}</em> a bien été modifié." : " L'accès au document <em>${doc['nom']}</em> n'a pas pu être modifié. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'».';
+
+      // Déplacement dans un autre répertoire
+      if ( is_numeric($parent = $_REQUEST['parent']) && ( $parent != $doc['parent'] ) )  {
+        $resultat = $mysqli->query("SELECT parents, matiere FROM reps WHERE id = $parent");
+        if ( $resultat->num_rows )  {
+          $r = $resultat->fetch_assoc();
+          $resultat->free();
+          if ( $mysqli->query("UPDATE docs SET parent = '$parent', parents = '${r['parents']},$parent',
+                               matiere = ${r['matiere']} WHERE id = $id") )  {
+            if ( $protection < 2 )  {
+              $mysqli->query("UPDATE reps SET nbfic = (nbfic -1) WHERE id = ${doc['parent']}");
+              $mysqli->query("UPDATE reps SET nbfic = (nbfic + 1) WHERE id = $parent");
+            }
+            $mysqli->query('ALTER TABLE docs ORDER BY parents,nom');
+            $message .= " Le document <em>${doc['nom']}</em> a bien été déplacé.";
+          }
+          else
+            $message .= " Le document <em>${doc['nom']}</em> n'a pas pu être déplacé. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'».';
+        }
+      }
+      
+      // Si modification(s), on met à jour les informations récentes éventuelles
+      if ( strlen($message) )  {
+        if ( $protection < 2 )  {
+          $resultat = $mysqli->query("SELECT GROUP_CONCAT( reps.nom ORDER BY FIND_IN_SET(reps.id,docs.parents) SEPARATOR '/' ) AS path
+                                      FROM docs LEFT JOIN reps ON FIND_IN_SET(reps.id,docs.parents) WHERE docs.id = $id");
+          $r = $resultat->fetch_assoc();
+          $resultat->free();
+          $path = $mysqli->real_escape_string("${r['path']}/${doc['nom']}");
+          // Document auparavant non visible
+          if ( $doc['protection'] == 2 )
+            recent($mysqli,3,$id,$path,"download?id=$id","<p>Nouveau document&nbsp;: $path".$mysqli->real_escape_string($doc['ext']).'</p>');
+          // Document auparavant visible : on met à jour les chemins, sans mettre en avant le document
+          // Remarque : le RSS n'est pas modifié, le sera leur d'une nouvelle information récente
+          else
+            $mysqli->query("UPDATE recents SET texte = REPLACE(texte,titre,'$path'), titre = '$path' WHERE id = 3000+$id");
+        }
+        // Si $protection = 2, on cherche à supprimer.
+        else
+          recent($mysqli,3,$id);
+      }
+      else
+        unset($message);
+    }
+  }
+  
+  // Suppression d'un document
+  elseif ( isset($_REQUEST['supprime_doc']) && ( $id ) )  {
+    if ( $mysqli->query("DELETE FROM docs WHERE id = $id") )  {
+      $message = "Le document <em>${doc['nom']}</em> a bien été supprimé.";
+      // Suppression physique
+      exec("rm -rf documents/${doc['lien']}");
+      // Mise à jour du répertoire
+      if ( $doc['protection'] < 2 )  {
+        $mysqli->query("UPDATE reps SET nbfic = (nbfic - 1) WHERE id = ${doc['parent']}");
+        // Suppression de l'éventuelle information récente
+        recent($mysqli,3,$id);
+      }
+    }
+    else
+      $message = "Le document <em>${doc['nom']}</em> n'a pas pu être supprimé. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'».';
+  }
+
+  // Mise à jour d'un document
+  elseif ( isset($_REQUEST['envoie']) && ( $id ) )  {
+    // Changement d'extension suspect donc interdit
+    $ext = ( strpos($_FILES['fichier']['name'],'.') ) ? strrchr($_FILES['fichier']['name'],'.') : '';
+    if ( $ext != $doc['ext'] )
+      $message = "Le document <em>${doc['nom']}</em> n'a pas été mis à jour&nbsp;: l'extension n'est pas celle attendue.";
+    else  {
+      // Déplacement du document uploadé au bon endroit
+      if ( move_uploaded_file($_FILES['fichier']['tmp_name'],"documents/${doc['lien']}/${doc['nom']}${doc['ext']}") )  {
+        // Gestion de la taille
+        $taille = intval($_FILES['fichier']['size']/1024);
+        $taille = ( $taille < 1024 ) ? "$taille&nbsp;ko" : intval($taille/1024).'&nbsp;Mo';
+        // Modifications dans la base de données
+        $mysqli->query("UPDATE docs SET upload = DATE(NOW()), taille = '$taille'");
+        // Information récente si document visible
+        if ( $doc['protection'] < 2 )  {
+          $resultat = $mysqli->query("SELECT GROUP_CONCAT( reps.nom ORDER BY FIND_IN_SET(reps.id,docs.parents) SEPARATOR '/' ) AS path
+                                      FROM docs LEFT JOIN reps ON FIND_IN_SET(reps.id,docs.parents) WHERE docs.id = $id");
+          $r = $resultat->fetch_assoc();
+          $resultat->free();
+          $path = $mysqli->real_escape_string("${r['path']}/${doc['nom']}");
+          recent($mysqli,3,$id,$path,"download?id=$id","<p>Nouvelle version du document&nbsp;: $path".$mysqli->real_escape_string($doc['ext'])."</p>");
+        }
+        $message = "Le document <em>${doc['nom']}</em> a bien été mis à jour.";
+      }
+      else
+        $message = "Le document <em>${doc['nom']}</em> n'a pas été mis à jour car le fichier envoyé n'est pas valide.";
+    }
+  }
+
+  // Envoi d'un nouveau document
+  elseif ( isset($_REQUEST['envoie']) )  {
+    // Vérifications des données envoyées (on fait confiance aux utilisateurs connectés pour ne pas envoyer de scripts malsains)
+    $nom = $_FILES['fichier']['name'];
+    $ext = ( strpos($nom,'.') ) ? strrchr($nom,'.') : '';
+    setlocale(LC_CTYPE, "fr_FR.UTF-8");
+    $nom = basename(str_replace($ext,'', ( strlen($_REQUEST['nom']) ) ? $_REQUEST['nom'] : $nom ));
+    $protection = ( in_array($_REQUEST['protection'],array(0,1,2)) ) ? $_REQUEST['protection'] : 0;
+    // Création du répertoire particulier
+    $lien = substr(sha1(mt_rand()),0,15);
+    while ( is_dir("documents/$lien") )
+      $lien = substr(sha1(mt_rand()),0,15);
+    mkdir("documents/$lien");
+    // Gestion de la taille
+    $taille = intval($_FILES['fichier']['size']/1024);
+    $taille = ( $taille < 1024 ) ? "$taille&nbsp;ko" : intval($taille/1024).'&nbsp;Mo';
+    // Récupération des données du répertoire parent
+    $resultat = $mysqli->query("SELECT parents, matiere FROM reps WHERE id = $rid");
+    $r = $resultat->fetch_assoc();
+    $resultat->free();
+    // Déplacement du document uploadé au bon endroit
+    if ( move_uploaded_file($_FILES['fichier']['tmp_name'],"documents/$lien/$nom$ext") )  {
+      // Écriture MySQL
+      if ( $mysqli->query("INSERT INTO docs SET parent = $rid, parents = '${r['parents']},$rid',
+                           matiere = ${r['matiere']}, nom = '".$mysqli->real_escape_string($nom)."',
+                           upload = DATE(NOW()), taille = '$taille', lien = '$lien', ext='".$mysqli->real_escape_string($ext)."', protection = $protection") )  {
+        $id = $mysqli->insert_id;
+        $mysqli->query('ALTER TABLE docs ORDER BY parents,nom');
+        $message = "Le document <em>$nom</em> a bien été mis en ligne.";
+        // Si le document est visible
+        if ( $protection < 2 )  {
+          // Mise à jour du répertoire
+          $mysqli->query("UPDATE reps SET nbfic = (nbfic + 1) WHERE id = $rid");
+          // Ajout d'une information récente
+          $resultat = $mysqli->query("SELECT GROUP_CONCAT( reps.nom ORDER BY FIND_IN_SET(reps.id,docs.parents) SEPARATOR '/' ) AS path
+                                      FROM docs LEFT JOIN reps ON FIND_IN_SET(reps.id,docs.parents) WHERE docs.id = $id");
+          $r = $resultat->fetch_assoc();
+          $resultat->free();
+          $path = $mysqli->real_escape_string("${r['path']}/$nom");
+          recent($mysqli,3,$id,$path,"download?id=$id","<p>Nouveau document&nbsp;: $path".$mysqli->real_escape_string($ext).'</p>');
+        }
+      }
+      else  {
+        // Retour en arrière
+        exec("rm -rf documents/$lien");
+        $message = "Le document <em>$nom</em> n'a pas été mis en ligne à cause d'une erreur lors des modifications dans la base de données MySQL. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'».';
+      }
+    }
+    else
+      $message = "Le document <em>$nom</em> n'a pas été mis en ligne car le fichier envoyé n'est pas valide.";
+  }
+  
+  // Mise à jour des champs 'docs' dans la table 'matieres' (pour le menu)
+  $mysqli->query('UPDATE matieres SET docs = (SELECT IF(nbfic+nbrep,1,0) FROM reps WHERE parent=0 AND matiere = matieres.id)');
+    
+  // Passage en connexion MySQL en lecture seulement
+  $mysqli->close();
+  $mysqli = new mysqli($serveur,$base,$mdp,$base);
+  $mysqli->set_charset('utf8');
+}
+
+//////////
+// HTML //
+//////////
+
+if ( $admin )  {
+  // Récupération des répertoires, dans le bon ordre pour un select
+  function liste($rid,$n)  {
+    $resultat = $GLOBALS['mysqli']->query("SELECT id, nom, parents FROM reps WHERE parent = $rid");
     while ( $r = $resultat->fetch_assoc() )  {
-      $parents .= "\n    <a href=\"?rep=${r['id']}\"><img class=\"icone\" src=\"icones/$icone.png\">${r['nom']}</a>";
-      $icone = 'next';
+      $GLOBALS['select_reps'] .= "        <option value=\"${r['id']}\">".str_repeat('&rarr;',$n)."${r['nom']}</option>\n";
+      liste($r['id'],$n+1);
     }
-    $parents .= "\n  </div>\n";
     $resultat->free();
   }
+  $select_reps = '';
+  liste(0,0);
+}
+
+// Récupération des données, vérification de la protection, répertoires parents
+$resultat = $mysqli->query("SELECT r.nom, r.parent, r.parents, r.nbrep + r.nbfic AS nb, r.protection, m.cle, m.nom AS mat
+                            FROM reps AS r LEFT JOIN matieres AS m ON r.matiere = m.id
+                            WHERE r.id = $rid");
+if ( $resultat->num_rows )  {
+  $rep = $resultat->fetch_assoc();
+  $resultat->free();
+
+  $p = ( is_null($rep['cle']) ) ? 'docs' : "docs?${rep['cle']}";
+  if ( $admin )
+    $t = "Gestion des documents - ${rep['nom']}";
   else
-    $parents = "\n  <div id=\"parents-vide\"> </div>\n";
+    $t = ( is_null($rep['cle']) ) ? 'Documents à télécharger' : "Documents à télécharger - ${rep['mat']}";
 
-  // Gestion de la protection : demande de login si le répertoire affiché est protégé
-  if ( $rep['protection'] && !$lecteur )
+  // Demander l'identification si le répertoire est protégé et l'utilisateur non connecté
+  if ( $rep['protection'] && !$lecteur && !$admin )
     include('login_lecture.php');
-  $rep['protection'] = 'open';
 
   // Haut de page, menu et message
   include('haut.php');
-  echo $parents;
+
+  // Répertoires parents
+  $resultat = $mysqli->query("SELECT id, nom, protection FROM reps WHERE FIND_IN_SET(id,'${rep['parents']},$rid')");
+  $parents = '';
+  while ( $r = $resultat->fetch_assoc() )
+    $parents .= "\n    <a href=\"?rep=${r['id']}\"><img class=\"icone\" src=\"icones/".( ( strlen($parents) ) ? 'next' : 'home' ).( ( $r['protection'] ) ? '-lock' : '' ).".png\">${r['nom']}</a>";
+  $resultat->free();
+  echo <<<FIN
+  
+  <div class="item" id="parents">$parents
+  </div>
+
+FIN;
 
   // Affichage du répertoire et de son contenu
-  affichage($rep,1);
+  if ( $rep['nb'] )  {
+    
+    function affichage($mysqli, $rid, $n, $lecteur,$admin)  {
+      $indent = str_pad('',2*$n);
+
+      // Sous-répertoires et récursivité
+      $resultat = $mysqli->query("SELECT id, nom, nbrep, nbfic, protection FROM reps WHERE parent = $rid");
+      if ( $resultat->num_rows )  {
+        while ( $r = $resultat->fetch_assoc() )  {
+          // Affichage du contenu si pas de protection ou utilisateur identifié
+          if ( ( !$r['protection'] ) || $lecteur || $admin )  {
+            if ( $r['nbrep']+$r['nbfic'] )
+              $contenu = str_replace(array('0 répertoire,',', 0 document'),'',"(${r['nbrep']} répertoire".( ( $r['nbrep'] > 1 ) ? 's' : '' ).", ${r['nbfic']} document".( ( $r['nbfic'] > 1 ) ? 's' : '' ).')');
+            else
+              $contenu = '(vide)';
+            $s = ( $admin && $r['protection'] ) ? '<p class="rep open lock"><img class="icone" src="icones/rep-open-lock.png">' : '<p class="rep open"><img class="icone" src="icones/rep-open.png">';
+            echo <<<FIN
+
+$indent<div class="rep">
+$indent  $s<a href="?rep=${r['id']}${GLOBALS['urladmin']}">${r['nom']}</a> $contenu</p>
+FIN;
+            affichage($mysqli,$r['id'],$n+1,$lecteur,$admin);
+            echo "\n$indent</div>";
+          }
+          else
+            echo <<<FIN
+
+$indent<div class="rep">
+$indent  <p class="rep lock"><img class="icone" src="icones/rep-lock.png"><a href="?rep=${r['id']}${GLOBALS['urladmin']}">${r['nom']}</a></p>
+$indent</div>
+FIN;
+        }
+        $resultat->free();
+      }
+
+      // Documents
+      $resultat = $mysqli->query("SELECT id, nom, taille, DATE_FORMAT(upload,'%d/%m/%Y') AS upload, ext, protection FROM docs WHERE parent = $rid AND (protection < 2 OR ".var_export($admin,true).')');
+      if ( $resultat->num_rows )  {
+        while ( $r = $resultat->fetch_assoc() )  {
+          switch ( $r['ext'] )  {
+            case '.pdf':
+              $icone = 'pdf';
+              break;
+            case '.doc':
+            case '.odt':
+            case '.docx':
+              $icone = 'doc';
+              break;
+            case '.xls':
+            case '.ods':
+            case '.xlsx':
+              $icone = 'xls';
+              break;
+            case '.ppt':
+            case '.odp':
+            case '.pptx':
+              $icone = 'ppt';
+              break;
+            case '.jpg':
+            case '.jpeg':
+            case '.png':
+            case '.gif':
+            case '.svg':
+            case '.tif':
+            case '.tiff':
+            case '.bmp':
+            case '.ps':
+            case '.eps':
+              $icone = 'jpg';
+              break;
+            case '.py':
+              $icone = 'python';
+              break;
+            case '.avi':
+            case '.mpeg':
+            case '.mpg':
+            case '.wmv':
+            case '.mp4':
+            case '.ogv':
+            case '.qt':
+            case '.mov':
+            case '.mkv':
+              $icone = 'avi';
+              break;
+            case '.mp3':
+            case '.ogg':
+            case '.oga':
+            case '.wma':
+            case '.wav':
+            case '.ra':
+            case '.rm':
+              $icone = 'mp3';
+              break;
+            case '.txt':
+            case '.rtf':
+              $icone = 'txt';
+              break;
+            case '.zip':
+            case '.rar':
+            case '.7z':
+              $icone = 'zip';
+              break;
+            case '.exe':
+            case '.sh':
+            case '':
+              $icone = 'exe';
+              break;
+            default :
+              $icone = 'defaut';
+          }
+          if ( $admin )  {
+            $id = $r['id'];
+            if ( $r['protection'] == 1 )
+              $icone .= '-lock';
+            $cache = ( $r['protection'] == 2 ) ? ' (non diffusé)' : '';
+            $select_reps = str_replace(array("\"$rid\"",'        '),array("\"$rid\" selected","$indent      "),$GLOBALS['select_reps']);
+            $select_protection = str_replace("\"${r['protection']}\"","\"${r['protection']}\" selected",'<option value="0">Visible de tous</option><option value="1">Visible après identification</option><option value="2">Non visible</option>');
+            echo <<<FIN
+
+$indent<div class="fic admin">
+$indent  <span><img class="icone" src="icones/$icone.png">${r['nom']}</span>$cache
+$indent  <form action="?rep=${GLOBALS['rid']}${GLOBALS['urladmin']}" method="post" enctype="multipart/form-data">
+$indent    <input class="bouton" type="submit" name="envoie" value="Envoyer">
+$indent    <p class="ligne"><label for="fichier$id">Mettre à jour&nbsp;: </label><input type="file" id="fichier$id" name="fichier"></p>
+$indent    <input type="hidden" name="id" value="$id">
+$indent  </form>
+$indent  <hr>
+$indent  <form action="?rep=${GLOBALS['rid']}${GLOBALS['urladmin']}" method="post">
+$indent    <p class="boutons"><input type="submit" name="modifie_doc" value="Valider" title="Valider les modifications"><input type="submit" name="supprime_doc" value="Supprimer" title="Supprimer ce document"></p>
+$indent    <p class="ligne"><label for="nomdoc$id">Nom à afficher&nbsp;: </label><input type="text" id="nom$id" name="nom" value="${r['nom']}" size="50"></p>
+$indent    <p class="ligne"><label for="parent$id">Répertoire&nbsp;: </label><select id="parent$id" name="parent">
+$select_reps$indent    </select></p>
+$indent    <p class="ligne"><label for="protecdoc$id">Accès&nbsp;: </label>
+$indent      <select id="protecdoc$id" name="protection">$select_protection</select>
+$indent    </p>
+$indent    <input type="hidden" name="id" value="$id">
+$indent    <p class="ligne"><label>Lien&nbsp;: </label><code>&lt;a href="download?id=$id"&gt;${r['nom']}&lt;/a&gt;</code></p>
+$indent    <p class="ligne"><a href="download?id=$id">Télécharger</a>|&nbsp;Taille&nbsp;: ${r['taille']}&nbsp;|&nbsp;Diffusé le ${r['upload']}</p>
+$indent  </form>
+$indent</div>
+FIN;
+          }
+          else  {
+            if ( $r['protection'] && !$lecteur )
+              $icone .= '-lock';
+            echo "
+$indent<p class=\"fic\"><a href=\"download?id=${r['id']}\"><img class=\"icone\" src=\"icones/$icone.png\">${r['nom']}</a> (${r['upload']}, ${r['taille']})</p>";
+          }
+        }
+        $resultat->free();
+      }
+    }
+
+    affichage($mysqli,$rid,1,$lecteur,$admin);
+    
+  }
+  else
+    echo "\n  <h3 class=\"warning\">Ce répertoire est vide.</h3>\n\n";
 }
 else  {
   // Haut de page, menu et message
   $p = 'docs';
   $t = 'Documents à télécharger';
   include('haut.php');
-  echo "\n<h2>Ce répertoire n'existe pas.</h2>";
+  echo "\n  <h3 class=\"warning\">Ce répertoire n'existe pas.</h3>\n\n";
 }
 
-$mysqli->close();
+// Interface d'administration : formulaires de modification
+if ( $admin )  {
+
+  // Taille maximale de fichier (pour l'aide)
+  $taille = min(ini_get('upload_max_filesize'),ini_get('post_max_size'));
+  if ( stristr($taille,'m') )
+    $taille = substr($taille,0,-1)*1048576;
+  elseif ( stristr($taille,'k') )
+    $taille = substr($taille,0,-1)*1024;
+  $taille = ( $taille < 1048576 ) ? intval($taille/1024).'&nbsp;ko' : intval($taille/1048576).'&nbsp;Mo';
+
+  // Restrictions pour les modifications sur le répertoire
+  $indication = $pas_suppr = $disabled = '';
+  if ( $rep['nb'] )  {
+    $pas_suppr = ' disabled';
+    $indication = "\n    <p>Ce répertoire ne peut pas être supprimé car il n'est pas vide.</p>";
+  }
+  if ( !$rep['parent'] )  {
+    $disabled = $pas_suppr = ' disabled';
+    $indication = "\n    <p>Ce répertoire est un répertoire racine, il ne peut être ni supprimé, ni renommé, ni déplacé.</p>";
+    $select_reps = '        <option></option>';
+  }  
+
+  // Choix par défaut de la protection du nouveau document et du sous-répertoire
+  $protection = (int)( $rep['protection'] || $_SESSION['protection'] );
+  $select_protection = str_replace("\"$protection\"","\"$protection\" selected",'<option value="0">Visible de tous</option><option value="1">Visible après identification</option>');
+
+  // HTML
 ?>
 
+  <div class="item aide">
+    <h3>Aide et explications</h3>
+    <p>Vous pouvez ici modifier le répertoire <?php echo $rep['nom']; ?> ainsi que l'ensemble des documents contenus dans les sous-répertoires. Les répertoires s'ouvrent en cliquant sur leur icone. Les documents sont modifiables en cliquant sur leur nom ou leur icone.</p>
+    <p>Les liens vers les répertoires et les documents ne sont jamais modifiés par un déplacement ou un changement de nom.</p>
+    <p>Le lien dans le menu vers la page de téléchargement est généré automatiquement, seulement si des documents sont disponibles.</p>
+    <h4>Répertoire <?php echo $rep['nom']; ?></h4>
+    <p>La case à cocher <em>Demande d'identification pour l'affichage du contenu</em> permet de restreindre la visibilité du répertoire aux visiteurs qui se sont identifiés. Dans ce cas, après <a href="utilisateurs">avoir créé un compte pour les élèves ici</a>, ces informations seront accessibles aux élèves auxquels vous aurez donné l'identifiant et le mot de passe choisis.</p>
+    <p>La case à cocher <em>Propager le choix ci-dessus à chaque document/sous-répertoire</em> permet de faire suivre le choix précédent à l'ensemble du contenu du répertoire. Si elle est décochée, aucune modification supplémentaire n'a lieu. Si elle est cochée, une action sera effectuée même si la précédente case n'a pas changé d'état.</p>
+    <p>Seuls les répertoires vides peuvent être supprimés. Le répertoire <em>Général</em> et les répertoires à la racine de chaque matière ne peuvent être renommés, déplacés ou supprimés.</p>
+    <h4>Nouveau document</h4>
+    <p>Vous pouvez envoyer des fichiers grâce au formulaire. Le <em>nom à afficher</em> sera le nom réellement affiché sur le site web (sans extension&nbsp;: elle est détectée automatiquement lors de l'envoi). Si vous laissez cette case vide, le nom du fichier envoyé sera récupéré.</p>
+    <p>Vous pouvez choisir la visibilité du document parmi trois possibilités&nbsp;:</p>
+    <ul>
+      <li><em>Visible de tous</em>&nbsp;: document accessible de tout visiteur</li>
+      <li><em>Visible après identification</em>&nbsp; document accessible uniquement après identification, par exemple pour que seuls vos élèves les voient. Il faut alors <a href="utilisateurs">créer un utilisateur de type élève</a>.</li>
+      <li><em>Non visible</em>&nbsp;: le document n'est pas encore visible en ligne. Cela peut être utile si vous souhaitez le mettre à disposition ultérieurement. C'est complètement sûr&nbsp;: vous pouvez mettre comme cela le prochain sujet de devoir ou même le corrigé.</li>
+    </ul>
+    <p>La taille du fichier envoyé est limitée à <?php echo $taille; ?>. Tout document est modifiable et déplaçable, sans que cela ne modifie le lien vers le document.</p>
+    <h4>Nouveau sous-répertoire</h4>
+    <p>Il est possible de créer autant de sous-répertoire que vous le souhaitez. La case à cocher <em>Demande d'identification pour l'affichage du contenu</em> permet de restreindre la visibilité du répertoire aux visiteurs qui se sont identifiés. Dans ce cas, après <a href="utilisateurs">avoir créé un compte pour les élèves ici</a>, ces informations seront accessibles aux élèves auxquels vous aurez donné l'identifiant et le mot de passe choisis.</p>
+    <h4>Modification des documents déjà envoyés</h4>
+    <p>Les documents sont modifiables en cliquant sur leur nom ou leur icone.</p>
+    <p>Vous pouvez <em>mettre à jour</em> un document déjà envoyé&nbsp;: cela évite de supprimer/recréer le document, et permet aux liens vers le document d'être toujours valables (l'adresse ne change pas).</p>
+    <p>Vous pouvez aussi renommer et déplacer tout document. Le lien correspondant ne changera pas.</p>
+    <p>Vous pouvez choisir la visibilité du document parmi trois possibilités&nbsp;:</p>
+    <ul>
+      <li><em>Visible de tous</em>&nbsp;: document accessible de tout visiteur</li>
+      <li><em>Visible après identification</em>&nbsp; document accessible uniquement après identification, par exemple pour que seuls vos élèves les voient. Il faut alors <a href="utilisateurs">créer un utilisateur de type élève</a>.</li>
+      <li><em>Non visible</em>&nbsp;: le document n'est pas encore visible en ligne. Cela peut être utile si vous souhaitez le mettre à disposition ultérieurement. C'est complètement sûr&nbsp;: vous pouvez mettre comme cela le prochain sujet de devoir ou même le corrigé.</li>
+    </ul>
+  </div>
+
+  <div class="item admin">
+  <form action="<?php echo "?rep=$rid$urladmin"; ?>" method="post">
+    <input class="bouton" type="submit" name="modifie" value="Valider" title="Valider les modifications">
+    <input class="bouton" type="submit" name="supprime" value="Supprimer" title="Supprimer ce répertoire"<?php echo $pas_suppr;?>>
+    <h3>Modifier le répertoire <em><?php echo $rep['nom']; ?></em></h3>
+    <p class="ligne"><label for="nomrep">Nom&nbsp;: </label><input type="text" id="nomrep" name="nom" value="<?php echo $rep['nom']; ?>" size="50"<?php echo $disabled;?>></p>
+    <p class="ligne"><label for="parent">Répertoire parent&nbsp;: </label>
+      <select id="parent" name="parent"<?php echo $disabled;?>>
+<?php echo str_replace("\"${rep['parent']}\"","\"${rep['parent']}\" selected",$select_reps); ?>
+      </select>
+    </p>
+    <p class="ligne"><label for="protecrep">Demande d'identification pour l'affichage du contenu&nbsp;: </label><input type="checkbox" id="protecrep" name="protection" value="1"<?php echo ( $rep['protection'] ) ? ' checked' : ''; ?>></p>
+    <p class="ligne"><label for="propagation">Propager le choix ci-dessus à chaque document/sous-répertoire&nbsp;: </label><input type="checkbox" id="propagation" name="propagation" value="1"></p><?php echo $indication;?>
+  </form>
+  </div>
+
+  <div class="item admin">
+  <form action="<?php echo "?rep=$rid$urladmin"; ?>" method="post" enctype="multipart/form-data">
+    <input class="bouton" type="submit" name="envoie" value="Envoyer">
+    <h3>Déposer un document dans le répertoire <em><?php echo $rep['nom']; ?></em></h3>
+    <p class="ligne"><label for="nomdoc">Nom à afficher&nbsp;: </label><input type="text" id="nomdoc" name="nom" value="" size="50"></p>
+    <p class="ligne"><label for="fichier">Fichier&nbsp;: </label><input type="file" id="fichier" name="fichier"></p>
+    <p class="ligne"><label for="protecdoc">Accès&nbsp;: </label>
+      <select id="protecdoc" name="protection"><?php echo $select_protection; ?><option value="2">Non visible</option></select>
+    </p>
+    <input type="hidden" name="id" value="0">
+  </form>
+  </div>
+
+  <div class="item admin">
+  <form action="<?php echo "?rep=$rid$urladmin"; ?>" method="post" enctype="multipart/form-data">
+    <input class="bouton" type="submit" name="cree" value="Créer">
+    <h3>Créer un sous-répertoire</h3>
+    <p class="ligne"><label for="nomssrep">Nom&nbsp;: </label><input type="text" id="nomssrep" name="nom" value="" size="50"></p>
+    <p class="ligne"><label for="protecssrep">Demande d'identification pour l'affichage du contenu&nbsp;: </label><input type="checkbox" id="protecssrep" name="protection" value="1"<?php echo ( $protection ) ? ' checked' : ''; ?>></p>
+  </form>
+  </div>
+
+  <script type="text/javascript">
+$( function() {
+  $('.fic span').replaceWith(function() { return '<a href="">'+$(this).html()+'</a>'; });
+  $('.fic > a').click( function () {
+    $(this).parent().find('form,hr').toggle();
+    $(this).parent().toggleClass('admin');
+    return false;
+  }).click();
+});
+  </script>
+<?php
+}
+
+$lock = ( $admin ) ? '' : ':not(.lock)';
+?>
 
   <script type="text/javascript">
 $( function() {
-  $('p.rep:not(.lock) img').css('cursor','pointer').click( function () {
+  $('p.rep<?php echo $lock; ?> img').css('cursor','pointer').click( function () {
     var p = $(this).parent();
     p.toggleClass('open');
-    if ( p.is('.open') )  {
-      $(this).attr('src','icones/rep-open.png');
-      p.parent().children('.fic,div.rep').css('display','block');
-    }
-    else  {
-      $(this).attr('src','icones/rep.png');
-      p.parent().children('.fic,div.rep').css('display','none');
-    }
+    p.parent().children('.fic,div.rep').toggle();
+    $(this).attr('src',function(i,val){ return val.replace(p.is('.open')?'rep':'rep-open',p.is('.open')?'rep-open':'rep'); });
   });
-
-  $('#contenu > div.rep > div.rep').find('p.rep:not(.lock) img').attr('src','icones/rep.png').parent().removeClass('open');
-  $('#contenu > div.rep > div.rep').find('.fic,div.rep').css('display','none');
-  $('#contenu > .rep').before('<p>Vous pouvez ouvrir ou fermer des répertoires en cliquant sur leur icone.</p>');
+  $('p.rep<?php echo $lock; ?> img').attr('src',function(i,val){return val.replace('rep-open','rep');}).parent().removeClass('open');
+  $('div.rep .fic,div.rep div.rep').hide();
+//  $('#parents').after('<p>Vous pouvez ouvrir ou fermer des répertoires en cliquant sur leur icone.</p>');
 });
   </script>
-
+  
 <?php
+$mysqli->close();
+
 // Bas de page
 include('bas.php');
+
+
+exit();
 ?>
+
+
+  <script type="text/javascript">
+$( function() {
+<?php
+  if ( $admin )  {
+?>
+  $('.fic span').replaceWith(function() { return '<a href="">'+$(this).html()+'</a>'; });
+  $('.fic a').click( function () {
+    $(this).parent().find('form,hr').toggle();
+    $(this).parent().toggleClass('admin');
+    return false;
+  }).click();
+});
+<?php
+  }
+?>
+  $('p.rep<?php echo $lock; ?> img').css('cursor','pointer').click( function () {
+    var p = $(this).parent();
+    p.toggleClass('open');
+    p.parent().children('.fic:not(form,hr),div.rep').toggle();
+    $(this).attr('src',function(i,val){ return val.replace(p.is('.open')?'rep':'rep-open',p.is('.open')?'rep-open':'rep'); });
+    
+  });
+  $('p.rep<?php echo $lock; ?> img').attr('src',function(i,val){return val.replace('rep-open','rep');}).parent().removeClass('open');
+  //$('div.rep .fic:not(form,hr),div.rep div.rep').hide();
+  $('#parents').after('<p>Vous pouvez ouvrir ou fermer des répertoires en cliquant sur leur icone.</p>');
+});
+  </script>
+
+<?php
diff -urN cahier-de-prepa2.2.0/download.php cahier-de-prepa3.0.0/download.php
--- cahier-de-prepa2.2.0/download.php	2012-09-01 17:46:31.000000000 +0200
+++ cahier-de-prepa3.0.0/download.php	2013-08-20 21:38:10.493111597 +0200
@@ -28,13 +28,10 @@
       // Répertoires parents
       $resultat = $mysqli->query("SELECT id, nom FROM reps WHERE FIND_IN_SET(id,'${f['parents']}') AND protection = 0");
       if ( $resultat->num_rows )  {
-        $icone = 'home';
-        $parents = "\n  <div class=\"item rep\" id=\"parents\">";
-        while ( $r = $resultat->fetch_assoc() )  {
-          $parents .= "\n    <a href=\"docs?rep=${r['id']}\"><img class=\"icone\" src=\"icones/$icone.png\">${r['nom']}</a>";
-          $icone = 'next';
-        }
-        $parents .= "\n  </div>\n";
+        $parents = '';
+        while ( $r = $resultat->fetch_assoc() )
+          $parents .= "\n    <a href=\"docs?rep=${r['id']}\"><img class=\"icone\" src=\"icones/".( ( strlen($parents) ) ? 'next' : 'home' ).".png\">${r['nom']}</a>";
+        $parents = "\n  <div class=\"item rep\" id=\"parents\">$parents\n  </div>\n";
         $resultat->free();
       }
       
@@ -156,6 +153,9 @@
       case '.sh':
         $type = 'text/x-sh';
         break;
+      case '.py':
+        $type = 'text/x-python';
+        break;
       default :
         $type = 'application/octet-stream';
     }
diff -urN cahier-de-prepa2.2.0/haut.php cahier-de-prepa3.0.0/haut.php
--- cahier-de-prepa2.2.0/haut.php	2013-01-04 10:18:48.000000000 +0100
+++ cahier-de-prepa3.0.0/haut.php	2013-08-20 23:46:19.025357631 +0200
@@ -28,112 +28,162 @@
 
 FIN;
 ?>
-<script type="text/x-mathjax-config">
-  MathJax.Hub.Config({tex2jax: {inlineMath: [["$","$"],["\\(","\\)"]]}});
-</script></head>
-<body><div id="global">
+  <script type="text/x-mathjax-config">
+    MathJax.Hub.Config({tex2jax: {inlineMath: [["$","$"],["\\(","\\)"]]}});
+  </script>
+</head>
+<body>
+<div id="global">
 
 <h1><?php echo $t; ?></h1>
 
-<div id="menu">
-  <div>
-<?php
-// Fabrication du menu : pages d'informations, page de téléchargement des
-// documents, pages des programmes de colles/cahiers de texte/téléchargement
-// pour chaque matière. L'interface d'administration rajoute ses propres pages.
-// $p doit être déjà définie comme étant la clé correspondant à la page.
+<div id="colonne">
 
-// Page d'accueil de l'interface d'administration et bouton de déconnexion
+  <div id="menu">
+<?php
+// Menu de l'interface d'administration et bouton de déconnexion
+// Rappel : $p doit être déjà définie (clé correspondant à la page)
 if ( $admin )  {
-  $monaccueil = ( $p == 'monaccueil' ) ? '<a id="actuel" href=".' : '<a href=".';
-  $monaccueil .= str_replace('&amp;','?',$urladmin);
+  
+  // Début : déconnexion, accueil, documents toutes matières
+  $actuel = array();
+  $actuel['accueil'] = ( $p == 'accueil_admin' ) ? ' id="actuel"' : '';
+  $actuel['prefs'] = ( $p == 'prefs' ) ? ' id="actuel"' : '';
+  $lien = str_replace('&amp;','?',$urladmin);
+  $actuel['docs'] = ( $p == 'docs' ) ? ' id="actuel"' : '';
   echo <<<FIN
-  <form id="deconnexion" action="" method="post"><input type="submit" name="deconnexion" value="Se déconnecter"></form>
-  $monaccueil">Mon accueil</a>
-  </div>
-  <div>
-    <h3>Modifier mes données</h3>
+    <div>
+      <form id="deconnexion" action="" method="post"><input type="submit" name="deconnexion" value="Se déconnecter"></form>
+      <a${actuel['accueil']} href=".$lien">Accueil</a>
+      <a${actuel['prefs']} href="prefs$lien">Préférences</a>
+      <a${actuel['docs']} href="docs$lien">Documents (toutes matières)</a>
+    </div>
 
 FIN;
-  $liens = array(
-    "colles?${_SESSION['mat']}" => "Mon programme de colles",
-    "cdt?${_SESSION['mat']}" => "Mon cahier de texte",
-    "docs_admin?${_SESSION['mat']}" => "Mes documents");
-  // Liens auquels on ajoute éventuellement "&admin" si besoin
-  foreach ( $liens as $k => $v )
-    if ( $p == $k )
-      echo "    <a id=\"actuel\" href=\"$k$urladmin\">$v</a>\n";
-    else
-      echo "    <a href=\"$k$urladmin\">$v</a>\n";
-  echo "  </div>\n  <div>\n    <h3>Modifier les pages d'informations</h3>\n";
-}
 
-// Récupération et affichage des pages
-$resultat = $mysqli->query('SELECT cle, nom FROM pages');
-while ( $r = $resultat->fetch_assoc() )  {
-  $actuel = ( $p == $r['cle'] ) ? ' id="actuel"' : '';
-  echo "    <a$actuel href=\".?${r['cle']}$urladmin\">${r['nom']}</a>\n";
-}
-$resultat->free();
+  // Matières à afficher dans le menu
+  $resultat = $mysqli->query("SELECT id, cle, nom, colles, cdt FROM matieres WHERE FIND_IN_SET(id,'${_SESSION['matieres']}') ORDER BY FIND_IN_SET(id,'${_SESSION['matieres']}')");
+  $matieres = array();
+  while ( $r = $resultat->fetch_assoc() )  {
+    // Stockage utilisé dans admin.php, prefs.php
+    $matieres[] = $r;
+    // Pour le menu
+    $actuel['colles'] = ( $p == "colles?${r['cle']}" ) ? ' id="actuel"' : '';
+    $actuel['cdt'] = ( $p == "cdt?${r['cle']}" ) ? ' id="actuel"' : '';
+    $actuel['docs'] = ( $p == "docs?${r['cle']}" ) ? ' id="actuel"' : '';
+    echo <<<FIN
+    <div>
+      <h3>Matière <em>${r['nom']}</em></h3>
+      <a${actuel['colles']} href="colles?${r['cle']}$urladmin">Programme de colles</a>
+      <a${actuel['cdt']} href="cdt?${r['cle']}$urladmin">Cahier de texte</a>
+      <a${actuel['docs']} href="docs?${r['cle']}$urladmin">Documents</a>
+    </div>
 
-if ( $admin )  {
-  echo "  </div>\n  <div>\n    <h3>Modifier les données globales du site</h3>\n";
+FIN;
+  }
+  $resultat->free();
+
+  // Pages d'informations
+  $resultat = $mysqli->query('SELECT cle, nom FROM pages');
+  $aff = '';
+  while ( $r = $resultat->fetch_assoc() )
+    $aff .= "\n      <a ".( ( $p == $r['cle'] ) ? 'id="actuel" ' : '' )."href=\".?${r['cle']}$urladmin\">${r['nom']}</a>";
+  $resultat->free();
+  echo <<<FIN
+    <div>
+      <h3>Pages d'informations</h3>$aff
+    </div>
+
+FIN;
+  
+  // Paramètres du site
+  $aff = '';
   $liens = array(
     "utilisateurs" => "Les utilisateurs",
     "matieres" => "Les matières",
     "pages" => "Les pages du site",
-    "docs_admin" => "Les documents (toutes matières)",
     "semaines" => "Les semaines du planning annuel");
-  foreach ( $liens as $k => $v )
-    if ( $p == $k )
-      echo "    <a id=\"actuel\" href=\"$k\">$v</a>\n";
-    else
-      echo "    <a href=\"$k\">$v</a>\n";
-  echo "  </div>\n  <div>\n    <a href=\"http://$site/\">Voir le site public (pensez à revenir vous déconnecter...)</a>";
+  foreach ( $liens as $a => $l )
+    $aff .= "\n      <a ".( ( $p == $a ) ? 'id="actuel" ' : '' )."href=\"$a\">$l</a>";
+  echo <<<FIN
+    <div>
+      <h3>Paramètres du site</h3>$aff
+    </div>
+    <div>
+      <a href="http://$site/">Voir le site public (pensez à revenir vous déconnecter...)</a>
+    </div>
+FIN;
+
 }
 else  {
+  // Menu de la partie publique : pages d'informations, page de
+  // téléchargement des documents, pages des programmes de colles/cahiers
+  // de texte/téléchargement pour chaque matière.
+  // Rappel : $p doit être déjà définie (clé correspondant à la page)
+
+  // Récupération et affichage des pages
+  $resultat = $mysqli->query('SELECT cle, nom FROM pages');
+  $aff = '';
+  while ( $r = $resultat->fetch_assoc() )
+    $aff .= "\n      <a ".( ( $p == $r['cle'] ) ? 'id="actuel" ' : '' )."href=\".?${r['cle']}\">${r['nom']}</a>";
+  $resultat->free();
   // Page de téléchargement
-  $actuel = ( $p == 'docs' ) ? ' id="actuel"' : '';
-  echo "    <a$actuel href=\"docs\">Documents à télécharger</a>\n  </div>\n";
+  $aff .= "\n      <a ".( ( $p == 'docs' ) ? 'id="actuel" ' : '' )."href=\"docs\">Documents à télécharger</a>";
+  echo <<<FIN
+    <div>$aff
+    </div>
+
+FIN;
 
   // Récupération et affichage des matières
-  $resultat = $mysqli->query('SELECT cle, nom, colles, cdt, docs FROM matieres WHERE colles+cdt+docs');
+  $resultat = $mysqli->query('SELECT cle, nom, MOD(colles,2) AS colles, MOD(cdt,2) AS cdt, docs
+                              FROM matieres WHERE MOD(colles,2)+MOD(cdt,2)+docs');
   if ( $resultat->num_rows )  {
     while ( $r = $resultat->fetch_assoc() )  {
-      echo "  <div>\n    <h3>${r['nom']}</h3>\n";
-      if ( $r['colles'] )  {
-        if ( $p == "colles?${r['cle']}" )
-          echo "    <a id=\"actuel\" href=\"colles?${r['cle']}\">Programme de colles</a>\n";
-        else
-          echo "    <a href=\"colles?${r['cle']}\">Programme de colles</a>\n";
-      }
-      if ( $r['cdt'] )  {
-        if ( $p == "cdt?${r['cle']}" )
-          echo "    <a id=\"actuel\" href=\"cdt?${r['cle']}\">Cahier de texte</a>\n";
-        else
-          echo "    <a href=\"cdt?${r['cle']}\">Cahier de texte</a>\n";
-      }
-      if ( $r['docs'] )  {
-        if ( $p == "docs?${r['cle']}" )
-          echo "    <a id=\"actuel\" href=\"docs?${r['cle']}\">Documents à télécharger</a>\n";
-        else
-          echo "    <a href=\"docs?${r['cle']}\">Documents à télécharger</a>\n";
-      }
-      echo "  </div>\n";
+      $aff = '';
+      if ( $r['colles'] )
+        $aff .= "\n      <a ".( ( $p == "colles?${r['cle']}" ) ? 'id="actuel" ' : '' )."href=\"colles?${r['cle']}\">Programme de colles</a>";
+      if ( $r['cdt'] )
+        $aff .= "\n      <a ".( ( $p == "cdt?${r['cle']}" ) ? 'id="actuel" ' : '' )."href=\"cdt?${r['cle']}\">Cahier de texte</a>";
+      if ( $r['docs'] )
+        $aff .= "\n      <a ".( ( $p == "docs?${r['cle']}" ) ? 'id="actuel" ' : '' )."href=\"docs?${r['cle']}\">Documents à télécharger</a>";
+      echo <<<FIN
+    <div>
+      <h3>${r['nom']}</h3>$aff
+    </div>
+
+FIN;
     }
     $resultat->free();
   }
   
   // Lien vers l'interface d'administration
   $proto = ( $https ) ? 'https://' : 'http://';
-  if ( strlen($siteadmin) )
-    echo "  <div>\n    <a href=\"$proto$siteadmin\">Administration du site</a>";
-  else
-    echo "  <div>\n    <a href=\"$proto$site/?admin\">Administration du site</a>";
+  echo "    <div>\n      <a href=\"".( ( strlen($siteadmin) ) ? $proto.$siteadmin : "$proto$site/?admin" )."\">Administration du site</a>\n    </div>";
 }
 ?>
 
   </div>
+
+<?php
+// Affichage des informations récentes : seulement sur la partie publique
+if ( !$admin )  {
+  $resultat = $mysqli->query('SELECT titre, lien FROM recents WHERE DATEDIFF(NOW(),heure) < 10 LIMIT 20');
+  if ( $resultat->num_rows )  {
+    $aff = '';
+    while ( $r = $resultat->fetch_assoc() )
+      $aff .= "\n    <a href=\"${r['lien']}\">${r['titre']}</a>";
+    $resultat->free();
+    echo <<<FIN
+  <div id="recent">
+    <h3>Informations récentes&nbsp;<a href="rss" id="rss"><img src="icones/rss.png" alt="Flux RSS"></a></h3>$aff
+  </div>
+
+
+FIN;
+  }
+}
+?>
 </div>
 
 <div id="contenu">
Les fichiers binaires cahier-de-prepa2.2.0/icones/big/python.png et cahier-de-prepa3.0.0/icones/big/python.png sont différents
Les fichiers binaires cahier-de-prepa2.2.0/icones/big/rss.png et cahier-de-prepa3.0.0/icones/big/rss.png sont différents
Les fichiers binaires cahier-de-prepa2.2.0/icones/big-lock/python-lock.png et cahier-de-prepa3.0.0/icones/big-lock/python-lock.png sont différents
Les fichiers binaires cahier-de-prepa2.2.0/icones/big-lock/rss-lock.png et cahier-de-prepa3.0.0/icones/big-lock/rss-lock.png sont différents
Les fichiers binaires cahier-de-prepa2.2.0/icones/list-add.png et cahier-de-prepa3.0.0/icones/list-add.png sont différents
Les fichiers binaires cahier-de-prepa2.2.0/icones/list-remove.png et cahier-de-prepa3.0.0/icones/list-remove.png sont différents
Les fichiers binaires cahier-de-prepa2.2.0/icones/python-lock.png et cahier-de-prepa3.0.0/icones/python-lock.png sont différents
Les fichiers binaires cahier-de-prepa2.2.0/icones/python.png et cahier-de-prepa3.0.0/icones/python.png sont différents
Les fichiers binaires cahier-de-prepa2.2.0/icones/rss-lock.png et cahier-de-prepa3.0.0/icones/rss-lock.png sont différents
Les fichiers binaires cahier-de-prepa2.2.0/icones/rss.png et cahier-de-prepa3.0.0/icones/rss.png sont différents
diff -urN cahier-de-prepa2.2.0/index.php cahier-de-prepa3.0.0/index.php
--- cahier-de-prepa2.2.0/index.php	2013-01-03 23:48:43.000000000 +0100
+++ cahier-de-prepa3.0.0/index.php	2013-08-24 01:00:19.893794171 +0200
@@ -8,7 +8,7 @@
 $resultat = $mysqli->query('SELECT id, cle, titre, nom, bandeau, protection FROM pages');
 if ( $resultat->num_rows )  {
   if ( !empty($_REQUEST) )  {
-    // Argument donné sous la forme page=cle valable (pour admin.php, en POST)
+    // Si l'argument est donné sous la forme page=cle (pour admin.php, en POST)
     if ( isset($_REQUEST['page']) )
       $_REQUEST[$_REQUEST['page']] = '';
     while ( $r = $resultat->fetch_assoc() )
@@ -18,7 +18,7 @@
         break;
       }
   }
-  // Page par défaut pour la partie publique : la page d'accueil
+  // Page par défaut pour la partie publique : la première page
   if ( !isset($page) )  {
     // Page par défaut pour l'interface d'administration : admin.php
     if ( $admin )
@@ -41,111 +41,103 @@
   $mysqli = new mysqli($serveur,"$base-adm",$mdp,$base);
   $mysqli->set_charset('utf8');
   
-  // Traitement d'un ajout/modification
-  if ( isset($_REQUEST['modifie']) )  {
-    // Vérification des données envoyées
-    if ( !strlen($_REQUEST['texte']) )
-      $message = 'Il n\'est pas possible de valider un texte vide. Pour supprimer une information, il faut cliquer sur Supprimer.';
-    else  {
-      // Sauvegarde de la table contenant les données
-      sauvegarde_mysql('infos');
+  // Traitement des modifications d'une information
+  if ( isset($_REQUEST['id']) && is_numeric($id = $_REQUEST['id']) )  {
+
+    // Vérification que l'identifiant est valide. Défaut : id=0 (nouvelle information)
+    if ( $id )  {
+      $resultat = $mysqli->query("SELECT id, ordre, cache, titre, texte, ( SELECT MAX(ordre) FROM infos WHERE page = $pid ) AS max
+                                  FROM infos WHERE page = $pid AND id = $id");
+      if ( $resultat->num_rows )  {
+        $r = $resultat->fetch_assoc();
+        $resultat->free();
+      }
+      else
+        $id = 0;
+    }
+
+    // Sauvegarde de la table contenant les données
+    sauvegarde_mysql('infos');
+
+    // Pour les informations récentes
+    $lien_recent = '.?'.addslashes($page['cle']);
+
+    // Traitement d'un ajout/modification
+    if ( isset($_REQUEST['modifie']) && strlen($_REQUEST['texte']) )  {
+
       // Validation des données envoyées
       $texte = $mysqli->real_escape_string($_REQUEST['texte']);
       $titre = $mysqli->real_escape_string($_REQUEST['titre']);
-      // Si identifiant, modification d'une info existante (pas de lien en page d'accueil)
-      if ( isset($_REQUEST['id']) && is_numeric($id = $_REQUEST['id']) )
-        $message = ( $mysqli->query("UPDATE infos SET texte = '$texte', titre = '$titre', auto = '' WHERE id = $id")
-        ) ? 'L\'information a bien été modifiée.' : 'L\'information n\'a pas pu être modifiée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'».';
-      // Sinon, nouvelle information (lien en page d'accueil)
+
+      // Si $id > 0 : modification d'une information existante. Si $id = 0, nouvelle information
+      if ( $id )  {
+        if ( $mysqli->query("UPDATE infos SET texte = '$texte', titre = '$titre' WHERE id = $id") )  {
+          $message = 'L\'information a bien été modifiée.';
+          // Ajout d'une information récente si information diffusée
+          if ( !$r['cache'] )
+            recent($mysqli,1,$id,"Info : $titre",$lien_recent,$texte);
+        }
+        else
+          $message = 'L\'information n\'a pas pu être modifiée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'».';
+      }
       else  {
         $cache = ( isset($_REQUEST['cache']) ) ? 1 : 0;
-        $message = ( $mysqli->query("UPDATE infos SET ordre = (ordre+1) WHERE page = $pid")
-                  && $mysqli->query("INSERT INTO infos SET ordre = 1, page = $pid, texte = '$texte', titre = '$titre', cache = $cache, auto = ''")
-                  && ( $id = $mysqli->insert_id )
-                  && $mysqli->query('ALTER TABLE infos ORDER BY ordre,page')
-        ) ? 'L\'information a bien été ajoutée.' : 'L\'information n\'a pas pu être ajoutée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'».';
-        // Si l'identifiant de la page est non nul, ce n'est pas la page d'accueil : lien vers la nouvelle info (si non cachée)
-        if ( $pid && $id && !$cache )
-          $message .= ( $mysqli->query('UPDATE infos SET ordre = (ordre+1) WHERE page = 0') 
-                     && $mysqli->query("INSERT INTO infos SET ordre = 1, page = 0, auto = 'info-$id', cache = 0, texte = '<p>Nouvelle information sur la page <a href=\".?".addslashes("${page['cle']}\">${page['nom']}")."</a>&nbsp;: $titre</p>', titre = '".date('d/m/y').'&nbsp;: nouvelle information\'')
-                     && $mysqli->query('ALTER TABLE infos ORDER BY ordre,page')
-          ) ? "<br>Un lien vers cette nouvelle information a été ajouté en haut de la page d'accueil. Ce lien est <a href=\".?accueil$urladmin\">modifiable/supprimable.</a>." : '';
+        if ( $mysqli->query("UPDATE infos SET ordre = (ordre+1) WHERE page = $pid")
+          && $mysqli->query("INSERT INTO infos SET ordre = 1, page = $pid, texte = '$texte', titre = '$titre', cache = $cache") )  {
+            $message = 'L\'information a bien été ajoutée.';
+            // Ajout d'une information récente si information diffusée
+            if ( !$cache )
+              recent($mysqli,1,$mysqli->insert_id,"Info : $titre",$lien_recent,$texte);
+            $mysqli->query('ALTER TABLE infos ORDER BY page,ordre');
+        }
+        else
+          $message = 'L\'information n\'a pas pu être ajoutée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'».';
       }
     }
-  }
 
-  // Traitement d'une transformation/suppression
-  elseif ( isset($_REQUEST['id']) && is_numeric($id = $_REQUEST['id']) )  {
-    
-    // Vérification de l'existence et récupération de l'ordre d'affichage
-    $resultat = $mysqli->query("SELECT ordre, titre FROM infos WHERE id = $id");
-    if ( $resultat->num_rows )  {
-      $r = $resultat->fetch_assoc();
-      $ordre = $r['ordre'];
-      $resultat->free();
-
-      // Sauvegarde de la table contenant les données
-      sauvegarde_mysql('infos');
+    elseif ( $id )  {
 
       // Déplacement vers le haut
-      if ( isset($_REQUEST['monte']) && ( $ordre > 1 ) )
-        $message = ( $mysqli->query("UPDATE infos SET ordre = (2*$ordre-1-ordre) WHERE ( ordre = $ordre OR ordre = ($ordre-1) ) AND page = $pid")
-                  && $mysqli->query('ALTER TABLE infos ORDER BY ordre,page')
+      if ( isset($_REQUEST['monte']) && ( $r['ordre'] > 1 ) )
+        $message = ( $mysqli->query("UPDATE infos SET ordre = (2*${r['ordre']}-1-ordre) WHERE ( ordre = ${r['ordre']} OR ordre = (${r['ordre']}-1) ) AND page = $pid")
+                  && $mysqli->query('ALTER TABLE infos ORDER BY page,ordre')
         ) ? 'L\'information a bien été montée d\'une place.' : 'L\'information n\'a pas pu être déplacée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'».';
 
       // Déplacement vers le bas
-      elseif ( isset($_REQUEST['descend']) )
-        $message = ( $mysqli->query("UPDATE infos SET ordre = (2*$ordre+1-ordre) WHERE ( ordre = $ordre OR ordre = ($ordre+1) ) AND page = $pid")
-                  && $mysqli->query('ALTER TABLE infos ORDER BY ordre,page')
+      elseif ( isset($_REQUEST['descend']) && ( $r['ordre'] < $r['max'] ) )
+        $message = ( $mysqli->query("UPDATE infos SET ordre = (2*${r['ordre']}+1-ordre) WHERE ( ordre = ${r['ordre']} OR ordre = (${r['ordre']}+1) ) AND page = $pid")
+                  && $mysqli->query('ALTER TABLE infos ORDER BY page,ordre')
         ) ? 'L\'information a bien été descendue d\'une place.' : 'L\'information n\'a pas pu être déplacée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'».';
 
-      // Positionnement "montré" (apparaît publiquement)
+      // Positionnement "montré" (apparaît sur la partie publique)
       elseif ( isset($_REQUEST['montre']) )  {
         if ( $mysqli->query("UPDATE infos SET cache = 0 WHERE id = $id") )  {
-          $message = 'L\'information a bien été réaffichée&nbsp;: elle apparaît à nouveau dans la partie publique.';
-          // Si l'identifiant de la page est non nul : lien vers la nouvelle info
-          if ( $pid )  {
-            $message .= ( $mysqli->query('UPDATE infos SET ordre = (ordre+1) WHERE page = 0') 
-                       && $mysqli->query("INSERT INTO infos SET ordre = 1, page = 0, auto = 'info-$id', cache = 0, texte = '<p>Nouvelle information sur la page <a href=\".?".addslashes("${page['cle']}\">${page['nom']}</a>&nbsp;: ${r['titre']}").'</p>\', titre = \''.date('d/m/y').'&nbsp;: nouvelle information\'')
-                       && $mysqli->query('ALTER TABLE infos ORDER BY ordre,page')
-            ) ? "<br>Un lien vers cette nouvelle information a été ajouté en haut de la page d'accueil. Ce lien est <a href=\".?accueil$urladmin\">modifiable/supprimable.</a>." : '';
-          }
+          $message = 'L\'information a bien été diffusée, elle apparaît désormais sur la partie publique.';
+          // Ajout d'information récente
+          recent($mysqli,1,$id,'Info : '.$mysqli->real_escape_string($r['titre']),$lien_recent,$mysqli->real_escape_string($r['texte']));
         }
         else
-          $message = 'L\'information n\'a pas pu être réaffichée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'».';
+          $message = 'L\'information n\'a pas pu être diffusée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'».';
       }
 
-      // Positionnement "caché" (n'apparaît plus publiquement)
+      // Positionnement "caché" (n'apparaît pas sur la partie publique)
       elseif ( isset($_REQUEST['cache']) )  {
         if ( $mysqli->query("UPDATE infos SET cache = 1 WHERE id = $id") )  {
-          $message = 'L\'information a bien été cachée&nbsp;: elle n\'apparaît plus dans la partie publique mais est toujours disponible ici pour modification ou réaffichage.';
-          // Suppression de l'éventuelle information en page d'accueil
-          $resultat = $mysqli->query("SELECT id, ordre FROM infos WHERE auto = 'info-$id'");
-          if ( $resultat->num_rows )  {
-            $r = $resultat->fetch_assoc();
-            $resultat->free();
-            $message .= ( $mysqli->query("DELETE FROM infos WHERE id = ${r['id']}")
-                       && $mysqli->query("UPDATE infos SET ordre = (ordre-1) WHERE ordre > ${r['ordre']} AND page = 0")
-            ) ? '<br>Le lien existant depuis la page d\'accueil a été automatiquement supprimé.' : '';
-          }
+          $message = 'L\'information n\'est plus diffusé&nbsp;: elle n\'apparaît plus sur la partie publique mais est toujours disponible ici pour modification ou diffusion.';
+          // Suppression de l'éventuelle information récente
+          recent($mysqli,1,$id);
         }
         else
           $message = 'L\'information n\'a pas pu être cachée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'».';
       }
 
       // Suppression
-      elseif ( isset($_REQUEST['supprime']) )  {
-        if ( $mysqli->query("DELETE FROM infos WHERE id = $id") && $mysqli->query("UPDATE infos SET ordre = (ordre-1) WHERE ordre > $ordre AND page = $pid") )  {
+      elseif ( isset($_REQUEST['supprime']) || !strlen($_REQUEST['texte']) )  {
+        if ( $mysqli->query("DELETE FROM infos WHERE id = $id") 
+          && $mysqli->query("UPDATE infos SET ordre = (ordre-1) WHERE ordre > ${r['ordre']} AND page = $pid") )  {
           $message = 'L\'information a bien été supprimée.';
-          // Suppression de l'éventuelle information en page d'accueil
-          $resultat = $mysqli->query("SELECT id, ordre FROM infos WHERE auto = 'info-$id'");
-          if ( $resultat->num_rows )  {
-            $r = $resultat->fetch_assoc();
-            $resultat->free();
-            $message .= ( $mysqli->query("DELETE FROM infos WHERE id = ${r['id']}")
-                       && $mysqli->query("UPDATE infos SET ordre = (ordre-1) WHERE ordre > ${r['ordre']} AND page = 0")
-            ) ? '<br>Le lien existant depuis la page d\'accueil a aussi été supprimé.' : '';
-          }
+          // Suppression de l'éventuelle information récente
+          recent($mysqli,1,$id);
         }
         else
           $message = 'L\'information n\'a pas pu être supprimée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'».';
@@ -155,19 +147,21 @@
 
   // Traitement des modifications de la page
   elseif ( isset($_REQUEST['modifie_page']) )  {
-    if ( !strlen($_REQUEST['nom']) )
-      $message = 'Il n\'est pas possible de valider une page sans clé ou sans nom. Pour supprimer une page, il faut cliquer sur Supprimer.';
+    if ( !strlen($_REQUEST['nom']) || !strlen($_REQUEST['cle']) )
+      $message = 'Il n\'est pas possible de valider une page sans clé ou sans nom.';
     else  {
       // Vérifications des données envoyées
       $nom = $mysqli->real_escape_string($_REQUEST['nom']);
       $titre = $mysqli->real_escape_string($_REQUEST['titre']);
+      $cle = $mysqli->real_escape_string($_REQUEST['cle']);
       $bandeau = $mysqli->real_escape_string($_REQUEST['bandeau']);
       $protection = ( isset($_REQUEST['protection']) ) ? 1 : 0;
       // Envoi des données
-      $message = ( $mysqli->query("UPDATE pages SET nom = '$nom', titre = '$titre',
-                                   bandeau = '$bandeau', protection = $protection WHERE cle = '${page['cle']}'")
+      $message = ( $mysqli->query("UPDATE pages SET nom = '$nom', titre = '$titre', cle = '$cle',
+                                   bandeau = '$bandeau', protection = $protection WHERE id = $pid")
       ) ? 'Les données de la page ont bien été modifiées.' : 'Les données de la page n\'ont pas pu être modifiées. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'».';
-      // Mise à jour pour l'affichage
+      
+      // Mise à jour des données de la page
       $resultat = $mysqli->query("SELECT cle, titre, nom, bandeau, protection FROM pages WHERE id = $pid");
       $page = $resultat->fetch_assoc();
       $resultat->free();
@@ -192,117 +186,122 @@
   // Haut de page, menu et message
   $t = "Modification de la page «&nbsp;${page['nom']}&nbsp;»";
   include('haut.php');
-  // Formulaire de modification des données de la page et aide générale
+
+  // Aide générale  
+?>
+
+  <div class="item aide">
+    <h3>Aide et explications</h3>
+    <p>Vous pouvez ci-dessous modifier les préférences associées à la page et ajouter/modifier les informations qui s'y trouvent.</p>
+    <h4>Préférences associées à la page</h4>
+    <ul>
+      <li>Le <em>titre</em> sera affiché en haut de page et dans la barre de titre du navigateur. Par exemple, «&nbsp;À propos de l'ADS et du TIPE&nbsp;».</li>
+      <li>Le <em>nom dans le menu</em> est affiché dans le menu en tant que lien vers la page. Il est préférable qu'il rentre sur une ligne, il faut donc le choisir assez court. Par exemple, «&nbsp;Informations ADS/TIPE&nbsp;».</li>
+      <li>La <em>clé</em> est un mot-clé qui est utilisé uniquement dans l'adresse de la page (<code>http://<?php echo $site; ?>/?[clé]</code>). Il vaut mieux que ce soit un mot unique, court et sans majuscule. Par exemple, «&nbsp;ads-tipe&nbsp;».</li>
+      <li>Le <em>texte de début</em> sera affiché au-dessus des informations de la page. Il s'agit d'une ou deux phrases maximum.</li>
+    </ul>
+    <p>La case à cocher <em>Demande d'identification pour l'affichage de la page </em> permet de restreindre la visibilité de la page aux visiteurs qui se sont identifiés. Dans ce cas, après <a href="utilisateurs">avoir créé un compte pour les élèves ici</a>, ces informations seront accessibles aux élèves auxquels vous aurez donné l'identifiant et le mot de passe choisis.</p>
+    <h4>Informations</h4>
+    <p>Pour l'ajout d'information, la case à cocher <em>Ne pas diffuser sur la partie publique</em> permet de cacher temporairement cette information, par exemple pour la diffuser ultérieurement.</p>
+    <p>Vous pouvez modifier le titre ou le texte d'une information existante, ou changer l'ordre d'apparition des différentes informations de la page.</p>
+    <p>Vous pouvez <em>cacher</em> ou <em>montrer</em> une information existante, c'est-à-dire la diffuser ou non sur la partie publique.</p>
+    <p>Vous pouvez supprimer une information existante par le bouton <em>Supprimer</em> ou en validant un texte vide.</p>
+  </div>
+  
+<?php
+
+  // Formulaire de modification des données de la page et formulaire d'ajout d'information
   $protection = ( $page['protection'] ) ? ' checked' : '';
-  $message = ( $pid ) ? "
-    <p>Ajouter une nouvelle information sur cette page créera automatiquement une information sur la page d'accueil, avec un lien vers cette page. Cette information «&nbsp;automatique&nbsp;» sera modifiable <a href=\"?accueil$urladmin\">en éditant la page d'accueil</a>.</p>" : '';
   echo <<<FIN
   <div class="item admin">
   <form action="?$p$urladmin" method="post">
     <input class="bouton" type="submit" name="modifie_page" value="Valider">
-    <h3>Modification de l'affichage de la page</h3>
+    <h3>Modifier les préférences de la page</h3>
     <p class="ligne"><label for="titre">Titre&nbsp;: </label><input type="text" id="titre" name="titre" value="${page['titre']}" size="80"></p>
     <p class="ligne"><label for="nom">Nom dans le menu&nbsp;: </label><input type="text" id="nom" name="nom" value="${page['nom']}" size="50"></p>
+    <p class="ligne"><label for="cle$id">Clé dans l'adresse&nbsp;: </label><input type="text" id="cle$id" name="cle" value="${page['cle']}" size="30"></p>
     <p class="ligne"><label for="bandeau">Texte de début&nbsp;:</label></p>
     <textarea id="bandeau" name="bandeau" rows="2" cols="100">${page['bandeau']}</textarea>
-    <p class="ligne"><label for="protection">Accès avec mot de passe&nbsp;: </label><input type="checkbox" id="protection" name="protection" value="1"$protection></p>
+    <p class="ligne"><label for="protection">Demande d'identification pour l'affichage de la page&nbsp;: </label><input type="checkbox" id="protection" name="protection" value="1"$protection></p>
   </form>
   </div>
 
-  <div class="item aide">
-    <p>Vous pouvez ci-dessus modifier l'affichage de la page. Pour modifier les autres pages, il faut aller <a href="pages">ici</a>. Si vous cochez l'<em>Accès avec mot de passe</em>, cette page ne pourra être vue que par les utilisateurs ayant entré un mot de passe. Les utilisateurs sont modifiables <a href"utilisateurs">ici</a>.</p>
-    <p>Vous pouvez ci-dessous ajouter une information sur cette page ou modifier les existantes. Toute nouvelle information apparaîtra automatiquement en haut de la page. L'ordre de l'ensemble des informations est modifiable.</p>
-    <p>Il est possible de «&nbsp;cacher&nbsp;» une information de la partie publique du site sans la supprimer réellement de la base de données. Cela peut être utile pour une information qui n'est plus d'actualité mais qui pourrait servir à nouveau ultérieurement.</p>$message
+  <div class="item admin">
+  <form action="?$p$urladmin" method="post">
+    <input class="bouton" type="submit" name="modifie" value="Valider" title="Valider les modifications de la page">
+    <h3>Ajouter une nouvelle information</h3>
+    <input class="ligne" type="text" name="titre" size=50 maxlength=65533 value="Titre de l'information">
+    <textarea name="texte" rows="10" cols="100">&lt;p&gt;Texte de l'information&lt;/p&gt;</textarea>
+    <p class="ligne"><label for="cache">Ne pas diffuser sur la partie publique&nbsp;: </label><input type="checkbox" id="cache" name="cache" value="1"></p>
+    <input type="hidden" name="id" value="0">
+  </form>
   </div>
 
-
 FIN;
 
-  // Fonction d'affichage
-  function affichage($r)  {
-    if ( $r['id'] )  {
-      $debut = "Information n°${r['ordre']}";
-      $valide1 = '';
-      $valide = "\n      <input type=\"submit\" name=\"modifie\" value=\"Valider\" title=\"Valider les modifications sur le titre ou le texte\">";
+  // Affichage des informations
+  $resultat = $mysqli->query("SELECT id, ordre, cache, titre, texte FROM infos WHERE page = $pid");
+  $mysqli->close();
+  if ( $max = $resultat->num_rows )  {
+    while ( $r = $resultat->fetch_assoc() )  {
       $monte = ( $r['ordre'] == 1 ) ? '' : "\n      <input type=\"submit\" name=\"monte\" value=\"&uarr;\" title=\"Remonter l'information dans l'ordre d'apparition\">";
-      $descend = ( $r['ordre'] == $GLOBALS['max'] ) ? '' : "\n      <input type=\"submit\" name=\"descend\" value=\"&darr;\" title=\"Descendre l'information dans l'ordre d'apparition\">";
-      $suppr = "\n      <input type=\"submit\" name=\"supprime\" value=\"Supprimer\" title=\"Supprimer l'information\">";
-      $cache_nouveau = '';
+      $descend = ( $r['ordre'] == $max ) ? '' : "\n      <input type=\"submit\" name=\"descend\" value=\"&darr;\" title=\"Descendre l'information dans l'ordre d'apparition\">";
       if ( $r['cache'] )  {
-        $debut .= ' (information cachée, non présente sur le site public)';
+        $debut .= ' (information non diffusée sur la partie publique)';
         $cache_classe = ' cache';
-        $cache = "\n      <input type=\"submit\" name=\"montre\" value=\"Montrer\" title=\"Montrer l'information, la rendre visible depuis le site public\">";
+        $cache = "\n      <input type=\"submit\" name=\"montre\" value=\"Montrer\" title=\"Diffuser l'information, la rendre visible sur la partie publique\">";
       }
       else  {
         $cache_classe = '';
-        $cache = "\n      <input type=\"submit\" name=\"cache\" value=\"Cacher\" title=\"Cacher l'information, la rendre invisible depuis le site public (L'information sera encore visible ici, par exemple pour une modification ultérieure)\">";
+        $cache = "\n      <input type=\"submit\" name=\"cache\" value=\"Cacher\" title=\"Ne plus diffuser l'information, la rendre invisible sur la partie publique\">";
       }
-    }
-    else  {
-      $debut = 'Nouvelle information';
-      $valide1 = "\n    <input class=\"bouton\" type=\"submit\" name=\"modifie\" value=\"Valider\" title=\"Valider les modifications sur le titre ou le texte\">";
-      $valide = $monte = $descend = $cache = $suppr = $cache_classe = '';
-      $cache_nouveau = "\n    <p class=\"ligne\"><label for=\"cache\">Ne pas publier immédiatement (information cachée)&nbsp;: </label><input type=\"checkbox\" id=\"cache\" name=\"cache\" value=\"1\"></p>";
-    }
-    echo <<<FIN
+      echo <<<FIN
+
   <div class="item admin$cache_classe">
-  <form action="?${GLOBALS['p']}${GLOBALS['urladmin']}" method="post">$valide1
-    <h3>$debut</h3>
-    <p class="boutons">$valide$monte$descend$cache$suppr
+  <form action="?$p$urladmin" method="post">
+    <h3>Information n°${r['ordre']}&nbsp;: ${r['titre']}</h3>
+    <p class="boutons">
+      <input type="submit" name="modifie" value="Valider" title="Valider les modifications sur le titre ou le texte">$monte$descend$cache
+      <input type="submit" name="supprime" value="Supprimer" title="Supprimer l'information">
     </p>
     <input class="ligne" type="text" name="titre" size=50 maxlength=65533 value="${r['titre']}">
-    <textarea name="texte" rows="10" cols="100">${r['texte']}</textarea>$cache_nouveau
+    <textarea name="texte" rows="10" cols="100">${r['texte']}</textarea>
     <input type="hidden" name="id" value="${r['id']}">
   </form>
   </div>
 
-
 FIN;
-  
-  }
-
-  // Formulaire vide pour une nouvelle information
-  affichage(array('id' => '', 'titre' => 'Titre de l\'information', 'texte' => '&lt;p&gt;Texte de l\'information à écrire ici&lt;/p&gt;'));
-
-  // Affichage des informations
-  $resultat = $mysqli->query("SELECT max(ordre) FROM infos WHERE page = $pid");
-  $r = $resultat->fetch_row();
-  $max = $r[0];
-  $resultat = $mysqli->query("SELECT id,ordre,cache,titre,texte,auto FROM infos WHERE page = $pid");
-  $mysqli->close();
-  if ( $resultat->num_rows )  {
-    while ( $r = $resultat->fetch_assoc() )
-      affichage($r);
+    }
     $resultat->free();
   }
   else
-    echo "  <h2>Il n'y actuellement aucune information enregistrée pour cette page.</h2>\n\n";
+    echo "\n  <h2>Il n'y actuellement aucune information enregistrée pour cette page.</h2>\n\n";
 
-  echo "  <script type=\"text/javascript\" src=\"textarea.js.php\"></script>\n\n";
+  echo "\n  <script type=\"text/javascript\" src=\"textarea.js.php\"></script>\n\n";
 }
 
 // Partie publique
 else  {
 
-  // Demander l'authentification si la page est protégée et l'utilisateur non connecté
+  // Demander l'identification si la page est protégée et l'utilisateur non connecté
   if ( $page['protection'] && !$lecteur )
     include('login_lecture.php');
 
   // Haut de page, menu et message
   include('haut.php');
-  // Affichage des informations non cachées
-  $resultat = $mysqli->query("SELECT titre,texte FROM infos WHERE page = $pid AND cache = 0");
+  // Affichage des informations diffusées
+  $resultat = $mysqli->query("SELECT titre, texte FROM infos WHERE page = $pid AND cache = 0");
   $mysqli->close();
   if ( $resultat->num_rows )  {
-    echo "  <h2>${page['bandeau']}</h2>\n\n";
+    echo "\n  <h2>${page['bandeau']}</h2>\n";
     while ( $r = $resultat->fetch_assoc() )
       echo <<<FIN
+
   <div class="item info">
     <h3>${r['titre']}</h3>
 ${r['texte']}
   </div>
 
-
 FIN;
     $resultat->free();
   }
diff -urN cahier-de-prepa2.2.0/installation.php cahier-de-prepa3.0.0/installation.php
--- cahier-de-prepa2.2.0/installation.php	2013-01-01 22:57:02.000000000 +0100
+++ cahier-de-prepa3.0.0/installation.php	2013-08-26 16:41:56.073131677 +0200
@@ -133,7 +133,8 @@
   `id` tinyint(2) unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
   `nom` varchar(50) NOT NULL,
   `mdp` char(40) NOT NULL,
-  `matiere` tinyint(2) unsigned NOT NULL
+  `matieres` varchar(15) NOT NULL,
+  `protection` tinyint(1) NOT NULL
 ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
 
 CREATE TABLE `pages` (
@@ -147,13 +148,12 @@
 ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
 
 CREATE TABLE `infos` (
-  `id` tinyint(3) unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
+  `id` smallint(4) unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
   `ordre` tinyint(2) unsigned NOT NULL,
   `page` tinyint(2) unsigned NOT NULL,
   `cache` tinyint(1) unsigned NOT NULL,
   `titre` text NOT NULL,
   `texte` text NOT NULL,
-  `auto` varchar(20) NOT NULL,
   KEY `ordre` (`ordre`,`page`),
   KEY `cache` (`cache`)
 ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
@@ -179,7 +179,7 @@
 ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
 
 CREATE TABLE `cdt` (
-  `id` smallint(5) unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
+  `id` smallint(3) unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
   `matiere` tinyint(2) unsigned NOT NULL,
   `semaine` tinyint(2) unsigned NOT NULL,
   `jour` date NOT NULL,
@@ -219,7 +219,7 @@
   KEY `matiere` (`matiere`)
 ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
 
-CREATE TABLE IF NOT EXISTS `reps` (
+CREATE TABLE `reps` (
   `id` tinyint(3) unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
   `parent` tinyint(3) unsigned NOT NULL,
   `parents` varchar(50) NOT NULL,
@@ -232,8 +232,8 @@
   KEY `matiere` (`matiere`)
 ) ENGINE=MyISAM  DEFAULT CHARSET=utf8;
 
-CREATE TABLE IF NOT EXISTS `docs` (
-  `id` smallint(4) unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
+CREATE TABLE `docs` (
+  `id` smallint(3) unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
   `parent` tinyint(3) unsigned NOT NULL,
   `parents` varchar(50) NOT NULL,
   `matiere` tinyint(2) unsigned NOT NULL,
@@ -247,13 +247,22 @@
   KEY `matiere` (`matiere`)
 ) ENGINE=MyISAM  DEFAULT CHARSET=utf8;
 
+CREATE TABLE `recents` (
+  `id` smallint(5) unsigned NOT NULL PRIMARY KEY,
+  `heure` datetime NOT NULL,
+  `titre` varchar(200) NOT NULL,
+  `lien` varchar(20) NOT NULL,
+  `texte` text NOT NULL,
+  KEY `heure` (`heure`)
+) ENGINE=MyISAM  DEFAULT CHARSET=utf8;
+
 FIN;
 
 $code_etape2 = <<<FIN
 TRUNCATE matieres;
 INSERT INTO matieres (id,ordre,cle,nom,colles,cdt,docs) VALUES (1,1,'[cle_matiere]','[nom_matiere]',0,0,0);
 TRUNCATE utilisateurs;
-INSERT INTO utilisateurs (nom,mdp,matiere) VALUES ('[utilisateur]',SHA1('[mot_de_passe]'),1);
+INSERT INTO utilisateurs (nom,mdp,matieres) VALUES ('[utilisateur]',SHA1('[mot_de_passe]'),'1');
 FIN;
 
 $c = addslashes($classe);
@@ -266,8 +275,6 @@
 TRUNCATE pages;
 INSERT INTO pages (ordre,cle,nom,titre,bandeau,protection)
   VALUES (1,'accueil','Accueil','La classe $c du lycée $l','Dernières informations importantes&nbsp;:',0);
-UPDATE pages SET id = 0;
-ALTER TABLE `pages` AUTO_INCREMENT =1;
 TRUNCATE `cdt-types`;
 INSERT INTO `cdt-types` (`matiere`, `ordre`, `cle`, `titre`, `h_fin`) VALUES
 (1, 1, 'cours', 'Cours', 1),
@@ -283,45 +290,10 @@
 -- suivi des définitions des semaines de la zone choisie
 
 FIN;
-if ( isset($_REQUEST['zone']) )
-  switch ( $_REQUEST['zone'] )  {
-    case 'A':
-    $code_etape3 .= <<<FIN
-INSERT INTO semaines (debut, colle, vacances) VALUES
-  ('2012-09-04',0,0),('2012-09-10',0,0),('2012-09-17',0,0),('2012-09-24',1,0),('2012-10-01',1,0),('2012-10-08',1,0),('2012-10-15',1,0),
-  ('2012-10-22',1,0),('2012-10-29',0,1),('2012-11-12',1,0),('2012-11-19',1,0),('2012-11-26',1,0),('2012-12-03',1,0),('2012-12-10',1,0),
-  ('2012-12-17',1,0),('2012-12-24',0,2),('2013-01-07',1,0),('2013-01-14',1,0),('2013-01-21',1,0),('2013-01-28',1,0),('2013-02-04',1,0),
-  ('2013-02-11',1,0),('2013-02-18',1,0),('2013-02-25',0,3),('2013-03-11',1,0),('2013-03-18',1,0),('2013-03-25',1,0),('2013-04-02',1,0),
-  ('2013-04-08',1,0),('2013-04-15',1,0),('2013-04-22',0,4),('2013-05-06',1,0),('2013-05-13',1,0),('2013-05-21',1,0),('2013-05-27',1,0),
-  ('2013-06-03',1,0),('2013-06-10',1,0),('2013-06-17',1,0),('2013-06-24',1,0),('2013-07-01',1,0);
-
-FIN;
-    break;
-    case 'B':
-    $code_etape3 .= <<<FIN
-INSERT INTO semaines (debut, colle, vacances) VALUES
-  ('2012-09-04',0,0),('2012-09-10',0,0),('2012-09-17',0,0),('2012-09-24',1,0),('2012-10-01',1,0),('2012-10-08',1,0),('2012-10-15',1,0),
-  ('2012-10-22',1,0),('2012-10-29',0,1),('2012-11-12',1,0),('2012-11-19',1,0),('2012-11-26',1,0),('2012-12-03',1,0),('2012-12-10',1,0),
-  ('2012-12-17',1,0),('2012-12-24',0,2),('2013-01-07',1,0),('2013-01-14',1,0),('2013-01-21',1,0),('2013-01-28',1,0),('2013-02-04',1,0),
-  ('2013-02-11',1,0),('2013-02-18',0,3),('2013-03-04',1,0),('2013-03-11',1,0),('2013-03-18',1,0),('2013-03-25',1,0),('2013-04-02',1,0),
-  ('2013-04-08',1,0),('2013-04-15',0,4),('2013-04-29',1,0),('2013-05-06',1,0),('2013-05-13',1,0),('2013-05-21',1,0),('2013-05-27',1,0),
-  ('2013-06-03',1,0),('2013-06-10',1,0),('2013-06-17',1,0),('2013-06-24',1,0),('2013-07-01',1,0);
-
-FIN;
-    break;
-    case 'C':
-    $code_etape3 .= <<<FIN
-INSERT INTO semaines (debut, colle, vacances) VALUES
-  ('2012-09-04',0,0),('2012-09-10',0,0),('2012-09-17',0,0),('2012-09-24',1,0),('2012-10-01',1,0),('2012-10-08',1,0),('2012-10-15',1,0),
-  ('2012-10-22',1,0),('2012-10-29',0,1),('2012-11-12',1,0),('2012-11-19',1,0),('2012-11-26',1,0),('2012-12-03',1,0),('2012-12-10',1,0),
-  ('2012-12-17',1,0),('2012-12-24',0,2),('2013-01-07',1,0),('2013-01-14',1,0),('2013-01-21',1,0),('2013-01-28',1,0),('2013-02-04',1,0),
-  ('2013-02-11',1,0),('2013-02-18',1,0),('2013-02-25',1,0),('2013-03-04',0,3),('2013-03-18',1,0),('2013-03-25',1,0),('2013-04-02',1,0),
-  ('2013-04-08',1,0),('2013-04-15',1,0),('2013-04-22',1,0),('2013-04-29',0,4),('2013-05-13',1,0),('2013-05-21',1,0),('2013-05-27',1,0),
-  ('2013-06-03',1,0),('2013-06-10',1,0),('2013-06-17',1,0),('2013-06-24',1,0),('2013-07-01',1,0);
-
-FIN;
-    break;
-  }
+if ( isset($_REQUEST['zone']) )  {
+  include('definition_semaines.php');
+  $code_etape3 .= $semaines;
+}
 
 ///////////////////////////////
 // Installation
@@ -392,21 +364,24 @@
     
     // Étape 2 : Vérification de l'existence d'au moins un administrateur
     else  {
-      $resultat = $mysqli->query('SELECT nom FROM utilisateurs LIMIT 1');
+      $resultat = $mysqli->query('SELECT id, nom, mdp, matieres, protection FROM utilisateurs LIMIT 1');
       if ( !($resultat->num_rows) )  $etape = 2;
       
       // Étape 2bis : Vérification de l'existence d'au moins une matière
       else  {
         $r = $resultat->fetch_assoc();
-        $_SESSION['user'] = $r['nom'];
+        $_SESSION['id'] = $r['id'];
+        $_SESSION['nom'] = $r['nom'];
+        $_SESSION['mdp'] = $r['mdp'];
+        $_SESSION['matieres'] = $r['matieres'];
+        $_SESSION['protection'] = $r['protection'];
         $resultat->free();
-        $resultat = $mysqli->query('SELECT cle FROM matieres LIMIT 1');
+        $resultat = $mysqli->query('SELECT id FROM matieres LIMIT 1');
         if ( !($resultat->num_rows) )  $etape = 2;
         
         // Étape 3 : Vérification de la présence d'au moins un répertoire
         else  {
           $r = $resultat->fetch_assoc();
-          $_SESSION['mat'] = $r['cle'];
           $resultat->free();
           $resultat = $mysqli->query('SELECT id FROM reps LIMIT 1');
           if ( !($resultat->num_rows) )  $etape = 3;
diff -urN cahier-de-prepa2.2.0/login_admin.php cahier-de-prepa3.0.0/login_admin.php
--- cahier-de-prepa2.2.0/login_admin.php	2013-01-11 16:13:49.000000000 +0100
+++ cahier-de-prepa3.0.0/login_admin.php	2013-08-15 16:39:01.506713173 +0200
@@ -11,12 +11,10 @@
 if ( isset($_REQUEST['login']) && isset($_REQUEST['motdepasse']) )  {
 
   // Récupération des logins/mdp dans la base MySQL et comparaison
-  // Seuls les logins/mdp correspondant à une matière sont acceptés (les autres
-  // sont des utilisateurs en lecture seulement)
+  // Seuls les logins/mdp correspondant à une/des matières sont acceptés
+  // (si matieres=0, utilisateurs en lecture seulement)
   $mysqli = premiere_connexion();
-  $resultat = $mysqli->query('SELECT utilisateurs.nom, utilisateurs.mdp, matieres.cle
-                              FROM utilisateurs LEFT JOIN matieres ON utilisateurs.matiere = matieres.id
-                              WHERE matieres.id IS NOT NULL');
+  $resultat = $mysqli->query('SELECT id, nom, mdp, matieres, protection FROM utilisateurs WHERE matieres');
   $mysqli->close();
   if ( $resultat->num_rows )  {
     while ( $r = $resultat->fetch_assoc() )
@@ -40,10 +38,13 @@
     // Pour vérification aux connexions ultérieures
     $_SESSION['client'] = $_SERVER['HTTP_USER_AGENT'];
     $_SESSION['ip'] = $_SERVER['REMOTE_ADDR'];
-    $_SESSION['user'] = $_POST['login'];
-    $_SESSION['mat'] = $r['cle'];
+    $_SESSION['id'] = $r['id'];
+    $_SESSION['nom'] = $r['nom'];
+    $_SESSION['mdp'] = $r['mdp'];
+    $_SESSION['matieres'] = $r['matieres'];
+    $_SESSION['protection'] = $r['protection'];
     $_SESSION['time'] = time()+900;
-    session_write_close();
+    // Remarque : pas de session_write_close() pour mettre à jour par prefs.php
     
     // Vérification du répertoire de sauvegarde des données MySQL
     if ( !is_dir('sauvegarde') || !is_executable('sauvegarde') || !is_writable('sauvegarde') )
@@ -86,7 +87,7 @@
 <p class="warning"><?php echo $message; ?></p>
 
 <form class="warning" action="" method="post">
-  <p class="ligne"><label for="login">Nom d'utilisateur&nbsp;: </label><input type="text" name="login" id="login"></p>
+  <p class="ligne"><label for="login">Identifiant&nbsp;: </label><input type="text" name="login" id="login"></p>
   <p class="ligne"><label for="motdepasse">Mot de passe&nbsp;: </label><input type="password" name="motdepasse" id="motdepasse"></p>
   <p><input type="submit" name="Ok" value="Envoyer"></p>
 </form>
diff -urN cahier-de-prepa2.2.0/login_lecture.php cahier-de-prepa3.0.0/login_lecture.php
--- cahier-de-prepa2.2.0/login_lecture.php	2012-08-28 22:51:31.000000000 +0200
+++ cahier-de-prepa3.0.0/login_lecture.php	2013-08-15 15:32:09.426584786 +0200
@@ -29,7 +29,7 @@
     // Pour vérification aux connexions ultérieures
     $_SESSION['client'] = $_SERVER['HTTP_USER_AGENT'];
     $_SESSION['ip'] = $_SERVER['REMOTE_ADDR'];
-    $_SESSION['user'] = $_POST['login'];
+    $_SESSION['login'] = $_POST['login'];
     $_SESSION['time'] = time()+3600;
     session_write_close();
     
@@ -60,7 +60,7 @@
 ?>
 
 <form class="warning" action="" method="post">
-  <p class="ligne"><label for="login">Nom d'utilisateur&nbsp;: </label><input type="text" name="login" id="login"></p>
+  <p class="ligne"><label for="login">Identifiant&nbsp;: </label><input type="text" name="login" id="login"></p>
   <p class="ligne"><label for="motdepasse">Mot de passe&nbsp;: </label><input type="password" name="motdepasse" id="motdepasse"></p>
   <p><input type="submit" name="Ok" value="Envoyer"></p>
 </form>
diff -urN cahier-de-prepa2.2.0/MAJSQL.sql cahier-de-prepa3.0.0/MAJSQL.sql
--- cahier-de-prepa2.2.0/MAJSQL.sql	1970-01-01 01:00:00.000000000 +0100
+++ cahier-de-prepa3.0.0/MAJSQL.sql	2013-08-23 02:02:43.551149158 +0200
@@ -0,0 +1,25 @@
+-- 
+-- Voilà les modifications à effectuer sur chaque base pour passer de Cahier de
+-- Prépa 2.2.0 à Cahier de Prépa 3.0.0.
+-- 
+
+DELETE FROM infos WHERE auto != '';
+UPDATE infos SET page = page+1 ORDER BY page DESC;
+UPDATE pages SET id = id+1 ORDER BY id DESC;
+UPDATE matieres SET cdt = cdt - MOD(cdt,2) + IF((SELECT id FROM cdt WHERE matiere = matieres.id AND cache = 0 LIMIT 1),1,0);
+UPDATE matieres SET colles = colles - MOD(colles,2) + IF((SELECT id FROM colles WHERE matiere = matieres.id AND cache = 0 LIMIT 1),1,0);
+UPDATE matieres SET docs = (SELECT IF(nbfic+nbrep,1,0) FROM reps WHERE parent=0 AND matiere = matieres.id);
+ALTER TABLE utilisateurs CHANGE matiere matieres VARCHAR( 15 ) NOT NULL;
+ALTER TABLE utilisateurs ADD protection TINYINT( 1 ) UNSIGNED NOT NULL;
+ALTER TABLE docs CHANGE id id SMALLINT( 4 ) UNSIGNED NOT NULL AUTO_INCREMENT;
+ALTER TABLE infos CHANGE id id SMALLINT( 4 ) UNSIGNED NOT NULL AUTO_INCREMENT;
+ALTER TABLE infos DROP auto;
+
+CREATE TABLE recents (
+  id smallint(5) unsigned NOT NULL PRIMARY KEY,
+  heure datetime NOT NULL,
+  titre varchar(50) NOT NULL,
+  lien varchar(20) NOT NULL,
+  texte text NOT NULL,
+  KEY heure (heure)
+) ENGINE=MyISAM  DEFAULT CHARSET=utf8;
diff -urN cahier-de-prepa2.2.0/matieres.php cahier-de-prepa3.0.0/matieres.php
--- cahier-de-prepa2.2.0/matieres.php	2013-01-01 23:22:08.000000000 +0100
+++ cahier-de-prepa3.0.0/matieres.php	2013-08-21 01:36:07.845568474 +0200
@@ -7,41 +7,51 @@
 ///////////////////
 // Modifications //
 ///////////////////
-if ( isset($_REQUEST['id']) )  {
-
+if ( isset($_REQUEST['id']) && is_numeric($id = $_REQUEST['id']) )  {
   // Connexion à la base de données
   $mysqli = new mysqli($serveur,"$base-adm",$mdp,$base);
   $mysqli->set_charset('utf8');
 
-  // Traitement d'un ajout/modification
+  // Vérification que l'identifiant est valide. Défaut : id=0 (nouvelle page)
+  if ( $id )  {
+    $resultat = $mysqli->query("SELECT id, nom, ordre, MOD(colles,2)+MOD(cdt,2)+docs AS nonvide, (SELECT MAX(m.ordre) FROM matieres AS m) AS max FROM matieres WHERE id = $id");
+    if ( $resultat->num_rows )  {
+      $r = $resultat->fetch_assoc();
+      $resultat->free();
+      $nom = $r['nom'];
+      $ordre = $r['ordre'];
+    }
+    else
+      $id = 0;
+  }
+
+  // Ajout ou modification d'une matière
   if ( isset($_REQUEST['modifie']) )  {
     // Vérification des données envoyées
     if ( !strlen($_REQUEST['cle']) || !strlen($_REQUEST['nom']) )
       $message = 'Il n\'est pas possible de valider une matière sans clé ou sans nom. Pour supprimer une matière, il faut cliquer sur Supprimer.';
     else  {
-      // Sauvegarde de la table contenant les données
+      // Sauvegarde des tables contenant les données
       sauvegarde_mysql('matieres');
+      sauvegarde_mysql('reps');
       // Validation des données envoyées
       $cle = $mysqli->real_escape_string($_REQUEST['cle']);
       $nom = $mysqli->real_escape_string($_REQUEST['nom']);
-      // Si identifiant numérique, modification d'une matière existante
-      if ( is_numeric($id = $_REQUEST['id']) )  {
-        $colles = ( isset($_REQUEST['colles']) ) ? ', colles = 2' : ", colles = IF((SELECT id FROM colles WHERE matiere = $id AND cache = 0 LIMIT 1),1,0)";
-        $cdt = ( isset($_REQUEST['cdt']) ) ? ', cdt = 2' : ", cdt = IF((SELECT id FROM cdt WHERE matiere = $id AND cache = 0 LIMIT 1),1,0)";
-        // Sauvegarde de la table contenant les données
-        sauvegarde_mysql('reps');
+      $colles = ", colles = IF((SELECT id FROM colles WHERE matiere = $id AND cache = 0 LIMIT 1),".( ( isset($_REQUEST['colles']) ) ? '3,2)' : '1,0)');
+      $cdt = ", cdt = IF((SELECT id FROM cdt WHERE matiere = $id AND cache = 0 LIMIT 1),".( ( isset($_REQUEST['cdt']) ) ? '3,2)' : '1,0)');
+      // Si identifiant numérique non nul, modification d'une matière existante
+      if ( $id )
         $message = ( $mysqli->query("UPDATE matieres SET cle = '$cle', nom = '$nom'$colles$cdt WHERE id = $id")
                   && $mysqli->query("UPDATE reps SET nom = '$nom' WHERE matiere = $id AND parent = 0")
-        ) ? 'La matière «&nbsp;'.stripslashes($nom).'&nbsp;» a bien été modifiée.' : 'La matière «&nbsp;'.stripslashes($nom).'&nbsp;» n\'a pas pu être modifiée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'».';
-      }
-      // Si id = '', ajout d'une nouvelle matière
+        ) ? 'La matière <em>'.stripslashes($nom).'</em> a bien été modifiée.' : "La matière <em>${r['nom']}</em> n'a pas pu être modifiée. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'».';
+      // Si id = 0, ajout d'une nouvelle matière
       else  {
         $colles = ( isset($_REQUEST['colles']) ) ? 2 : 0;
         $cdt = ( isset($_REQUEST['cdt']) ) ? 2 : 0;
         $message = ( $mysqli->query("INSERT INTO matieres SET cle = '$cle', nom = '$nom', colles = '$colles', cdt = '$cdt', docs = 0, ordre = ((SELECT MAX(m.ordre) FROM matieres AS m)+1)")
                   && ( $id = $mysqli->insert_id )
                   && $mysqli->query("INSERT INTO reps SET parent = 0, parents = 0, nom = '$nom', matiere = $id, nbrep = 0, nbfic = 0, protection = 0")
-                  && $mysqli->query("INSERT INTO `cdt-types` (`matiere`, `ordre`, `cle`, `titre`, `h_fin`) VALUES
+                  && $mysqli->query("INSERT INTO `cdt-types` (matiere, ordre, cle, titre, h_fin) VALUES
                                                              ($id, 1, 'cours', 'Cours', 1),
                                                              ($id, 2, 'TD', 'Séance de travaux dirigés', 1),
                                                              ($id, 3, 'TP', 'Séance de travaux pratiques', 1),
@@ -49,76 +59,50 @@
                                                              ($id, 5, 'interros', 'Interrogation de cours', 0),
                                                              ($id, 6, 'distributions', 'Distribution de document', 0),
                                                              ($id, 7, 'DM', 'Devoir maison', 0)")
-        ) ? 'La matière «&nbsp;'.stripslashes($nom).'&nbsp;» a bien été ajoutée.' : 'La matière «&nbsp;'.stripslashes($nom).'&nbsp;» n\'a pas pu être ajoutée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'».';
+        ) ? 'La matière <em>'.stripslashes($nom).'</em> a bien été ajoutée.' : 'La matière <em>'.stripslashes($nom).'</em> n\'a pas pu être ajoutée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'».';
       }
     }
   }
 
-  // Traitement d'une transformation/suppression
-  elseif ( is_numeric($id = $_REQUEST['id']) )  {
+  elseif ( $id )  {
     
-    // Vérification de l'existence et récupération de l'ordre d'affichage
-    $resultat = $mysqli->query("SELECT ordre, nom, (SELECT MAX(ordre) FROM matieres) AS max FROM matieres WHERE id = $id");
-    if ( $resultat->num_rows )  {
-      $r = $resultat->fetch_assoc();
-      $ordre = $r['ordre'];
-      $nom = $r['nom'];
-      $resultat->free();
-
-      // Sauvegarde de la table contenant les données
-      sauvegarde_mysql('matieres');
+    // Sauvegarde de la table contenant les données
+    sauvegarde_mysql('matieres');
 
-      // Déplacement vers le haut
-      if ( isset($_REQUEST['monte']) && ( $ordre > 1 ) )
-        $message = ( $mysqli->query("UPDATE matieres SET ordre = (2*$ordre-1-ordre) WHERE ordre = $ordre OR ordre = ($ordre-1)")
-                  && $mysqli->query('ALTER TABLE matieres ORDER BY ordre')
-        ) ? "La matière «&nbsp;$nom&nbsp;» a bien été montée d'une place." : "La matière «&nbsp;$nom&nbsp;» n'a pas pu être déplacée. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'».';
-      // Déplacement vers le bas
-      elseif ( isset($_REQUEST['descend']) && ( $ordre < $r['max'] ) )
-        $message = ( $mysqli->query("UPDATE matieres SET ordre = (2*$ordre+1-ordre) WHERE ordre = $ordre OR ordre = ($ordre+1)")
-                  && $mysqli->query('ALTER TABLE matieres ORDER BY ordre')
-        ) ? "La matière «&nbsp;$nom&nbsp;» a bien été descendue d'une place." : "La matière «&nbsp;$nom&nbsp;» n'a pas pu être déplacée. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'».';
-      // Suppression (impossible s'il ne reste qu'une matière)
-      elseif ( isset($_REQUEST['supprime']) && ( $r['max'] > 1 ) )  {
-        // Demande de confirmation
-        if ( !isset($_REQUEST['confirme']) )  {
-          $message = "
-        <form action=\"\" method=\"post\">
-          <p>Souhaitez-vous réellement supprimer la matière «&nbsp;${r['nom']}&nbsp;»&nbsp;? Cela entraînera la suppression de toutes les entrées du cahier de texte, de tous les programmes de colles et de tous les documents qui s'y rapportent. Ces suppressions seront définitives.</p>
-          <p>Si un utilisateur est associé à cette matière, il deviendra associé à la première matière de la liste. Ceci sera modifiable <a href=\"utilisateurs". str_replace('&amp;','?',$urladmin) ."\">sur la page de modification des utilisateurs</a>.</p>
-          <input type=\"submit\" name=\"confirme\" value=\"Oui\">
-          <input type=\"hidden\" name=\"supprime\" value=\"Supprimer\">
-          <input type=\"hidden\" name=\"id\" value=\"$id\">
-        </form>\n";
-        }
-        else  {
-          // Sauvegarde des tables contenant les données
-          sauvegarde_mysql('colles');
-          sauvegarde_mysql('cdt');
-          sauvegarde_mysql('reps');
-          sauvegarde_mysql('docs');
-          // Liste des fichiers à supprimer
-          $resultat = $mysqli->query("SELECT GROUP_CONCAT(CONCAT('documents/',lien) SEPARATOR ' ') FROM docs WHERE matiere = $id");
-          $r = $resultat->fetch_row();
-          if ( $mysqli->query("DELETE FROM matieres WHERE id = $id")
-            && $mysqli->query("UPDATE matieres SET ordre = (ordre-1) WHERE ordre > $ordre")
-            && $mysqli->query("DELETE FROM colles WHERE matiere = $id")
-            && $mysqli->query("DELETE FROM cdt WHERE matiere = $id")
-            && $mysqli->query("DELETE FROM `cdt-types` WHERE matiere = $id")
-            && $mysqli->query("DELETE FROM `cdt-seances` WHERE matiere = $id")
-            && $mysqli->query("DELETE FROM reps WHERE matiere = $id")
-            && $mysqli->query("DELETE FROM docs WHERE matiere = $id")
-            && $mysqli->query("UPDATE utilisateurs SET matiere = (SELECT m.id FROM matieres AS m ORDER BY m.ordre LIMIT 1) WHERE matiere = $id") )  {
-            $message = "La matière «&nbsp;$nom&nbsp;» a bien été supprimée, ainsi que l'ensemble des programmes de colles, du cahier de texte et des documents associés.";
-            // Suppression physique
-            if ( strlen($r[0]) )
-              exec("rm -rf ${r[0]}");
-          }
-          else
-            $message = "La matière «&nbsp;$nom&nbsp;» n'a pas pu être supprimée. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'».';
+    // Déplacement vers le haut
+    if ( isset($_REQUEST['monte']) && ( $ordre > 1 ) )
+      $message = ( $mysqli->query("UPDATE matieres SET ordre = (2*$ordre-1-ordre) WHERE ordre = $ordre OR ordre = ($ordre-1)")
+                && $mysqli->query('ALTER TABLE matieres ORDER BY ordre')
+      ) ? "La matière <em>$nom</em> a bien été montée d'une place." : "La matière <em>$nom</em> n'a pas pu être déplacée. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'».';
+
+    // Déplacement vers le bas
+    elseif ( isset($_REQUEST['descend']) && ( $ordre < $r['max'] ) )
+      $message = ( $mysqli->query("UPDATE matieres SET ordre = (2*$ordre+1-ordre) WHERE ordre = $ordre OR ordre = ($ordre+1)")
+                && $mysqli->query('ALTER TABLE matieres ORDER BY ordre')
+      ) ? "La matière <em>$nom</em> a bien été descendue d'une place." : "La matière <em>$nom</em> n'a pas pu être déplacée. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'».';
+
+    // Suppression
+    elseif ( isset($_REQUEST['supprime']) && ( $r['max'] > 1 ) )  {
+      // La suppression est impossible si la matière n'est pas vide
+      if ( $r['nonvide'] )
+        $message = "La matière <em>$nom</em> n'a pas pu être supprimée&nbsp;: ses programmes de colles, son cahier de texte ou ses documents ne sont pas vides. Il faut supprimer l'ensemble de ces données pour pouvoir supprimer une matière.";
+      // La suppression est impossible si la matière est associée à qqun
+      else  {
+        $resultat = $mysqli->query("SELECT id FROM utilisateurs WHERE FIND_IN_SET($id,matieres)");
+        if ( $resultat->num_rows )  {
+          $message = "La matière <em>$nom</em> n'a pas pu être supprimée&nbsp;: elle est associée à au moins un utilisateur. Il faut que chaque utilisateur concerné supprime lui-même son association à cette matière.";
+          $resultat->free();
         }
+        else
+          $message = ( $mysqli->query("DELETE FROM matieres WHERE id = $id")
+                    && $mysqli->query("UPDATE matieres SET ordre = (ordre-1) WHERE ordre > $ordre")
+                    && $mysqli->query("DELETE FROM `cdt-types` WHERE matiere = $id")
+                    && $mysqli->query("DELETE FROM `cdt-seances` WHERE matiere = $id")
+                    && $mysqli->query("DELETE FROM reps WHERE matiere = $id")
+          ) ? "La matière <em>$nom</em> a bien été supprimée." : "La matière <em>$nom</em> n'a pas pu être supprimée. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'».';
       }
     }
+
   }
   $mysqli->close();
 }
@@ -132,19 +116,40 @@
 $mysqli = new mysqli($serveur,$base,$mdp,$base);
 $mysqli->set_charset('utf8');
 include('haut.php');
+?>
 
+  <div class="item aide">
+    <h3>Aide et explications</h3>
+    <p>Vous pouvez ci-dessous ajouter une matière ou modifier les matières existantes.</p>
+    <p>L'ordre d'apparition des matières dans le menu est modifiable grâce aux flèches. Il s'agit du menu de la partie publique uniquement&nbsp;: si vous avez plusieurs matières dans votre interface d'administration, vous pouvez gérer leur ordre d'apparition dans <a href="prefs">vos préférences</a>.</p>
+    <p>La suppression d'une matière n'est possible, afin d'éviter les erreurs de manipulation lourdes de conséquences, que si cela est sans conséquences. Il faut donc qu'aucun programme de colles/cahier de texte/document ne soit enregistré. Il faut aussi que cette matière n'apparaissent pas dans l'interface d'administration, pour tous les utilisateurs&nbsp;: chaque utilisateur peut faire ce choix dans ses préférences.</p>
+    <h4>Préférences de chaque matière</h4>
+    <p>Le <em>nom complet</em> s'affiche dans le menu et dans les titres des pages.</p>
+    <p>La <em>clé dans l'adresse</em> est un mot-clé utilisé uniquement dans l'adresse des pages associées à la matière. Il vaut mieux que ce soit un mot unique, court et sans majuscule. Par exemple, «&nbsp;maths&nbsp;», «&nbsp;phys&nbsp;»...</p>
+    <p>Les deux cases à cocher <em>Demande d'identification pour l'affichage...</em> permettent de restreindre la visibilité du programme de colles ou du cahier de texte aux visiteurs qui se sont identifiés. Dans ce cas, après <a href="utilisateurs">avoir créé un compte pour les élèves ici</a>, ces informations seront accessibles aux élèves auxquels vous aurez donné l'identifiant et le mot de passe choisis.</p>
+    <p>Les liens vers le cahier de texte, les programmes de colles et les documents sont affichés automatiquement dans le menu de la partie publique, uniquement lorsqu'il y a derrière quelque chose à afficher.</p>
+  </div>
+
+<?php
 // Fonction d'affichage
 function affichage($r)  {
   if ( $id = $r['id'] )  {
-    $debut = "Matière n°${r['ordre']}";
+    $debut = "Matière n°${r['ordre']}&nbsp;: ${r['nom']}";
     $valide1 = '';
     $valide = "\n      <input type=\"submit\" name=\"modifie\" value=\"Valider\" title=\"Valider les modifications\">";
-    $monte = ( $r['ordre'] == 1 ) ? '' : "\n      <input type=\"submit\" name=\"monte\" value=\"&uarr;\" title=\"Remonter la matière dans l'ordre d'apparition\">";
-    $descend = ( $r['ordre'] == $GLOBALS['max'] ) ? '' : "\n      <input type=\"submit\" name=\"descend\" value=\"&darr;\" title=\"Descendre la matière dans l'ordre d'apparition\">";
-    $suppr = ( $GLOBALS['max'] == 1 ) ? '' : "\n      <input type=\"submit\" name=\"supprime\" value=\"Supprimer\" title=\"Supprimer la matière\">";
+    $monte = ( $r['ordre'] > 1 ) ? "\n      <input type=\"submit\" name=\"monte\" value=\"&uarr;\" title=\"Remonter la matière dans l'ordre d'apparition\">" : '';
+    $descend = ( $r['ordre'] < $GLOBALS['max'] ) ? "\n      <input type=\"submit\" name=\"descend\" value=\"&darr;\" title=\"Descendre la matière dans l'ordre d'apparition\">" : '';
+    if ( $r['nonvide'] )  {
+      $suppr = ( $GLOBALS['max'] > 1 ) ? "\n      <input type=\"submit\" name=\"supprime\" value=\"Supprimer\" title=\"Supprimer la matière\" disabled>" : '';
+      $indication = ( $GLOBALS['max'] > 1 ) ? "\n    <p>Cette matière ne peut pas être supprimée car ses programmes de colles, son cahier de texte ou des documents ne sont pas vides.</p>" : '';
+    }
+    else  {
+      $suppr = ( $GLOBALS['max'] > 1 ) ? "\n      <input type=\"submit\" name=\"supprime\" value=\"Supprimer\" title=\"Supprimer la matière\">" : '';
+      $indication = '';
+    }
   }
   else  {
-    $debut = 'Créer une nouvelle matière';
+    $debut = 'Nouvelle matière';
     $valide1 = "\n    <input class=\"bouton\" type=\"submit\" name=\"modifie\" value=\"Valider\" title=\"Valider les modifications\">";
     $valide = $monte = $descend = $suppr = '';
   }
@@ -156,9 +161,9 @@
     </p>
     <p class="ligne"><label for="nom$id">Nom complet&nbsp;: </label><input type="text" id="nom$id" name="nom" value="${r['nom']}" size="50"></p>
     <p class="ligne"><label for="cle$id">Clé dans l'adresse&nbsp;: </label><input type="text" id="cle$id" name="cle" value="${r['cle']}" size="30"></p>
-    <p class="ligne"><label for="colles$id">Affichage du programme de colle avec mot de passe</label><input type="checkbox" id="colles$id" name="colles" value="1"${r['colles']}></p>
-    <p class="ligne"><label for="cdt$id">Affichage du cahier de texte avec mot de passe</label><input type="checkbox" id="cdt$id" name="cdt" value="1"${r['cdt']}></p>
-    <input type="hidden" name="id" value="$id">
+    <p class="ligne"><label for="colles$id">Demande d'identification pour l'affichage du programme de colle</label><input type="checkbox" id="colles$id" name="colles" value="1"${r['colles']}></p>
+    <p class="ligne"><label for="cdt$id">Demande d'identification pour l'affichage du cahier de texte</label><input type="checkbox" id="cdt$id" name="cdt" value="1"${r['cdt']}></p>
+    <input type="hidden" name="id" value="$id">$indication
   </form>
   </div>
 
@@ -167,25 +172,13 @@
 }
 
 // Formulaire vide pour une nouvelle matière et aide générale
-affichage(array('id' => '', 'ordre' => 0, 'cle' => 'cle_de_la_matiere', 'nom' => 'Nom de la matière', 'colles' => '', 'cdt' => ''));
-?>
-
-  <div class="item aide">
-    <p>Vous pouvez ci-dessus ajouter une matière, ci-dessous modifier les existantes ou leur ordre d'affichage dans le menu.</p>
-    <p>Supprimer une matière supprime automatiquement toutes les entrées du cahier de texte, tous les programmes de colles, tous les répertoires et documents qui s'y rapportent. Une confirmation est demandée avant la suppression.</p>
-    <p>Le <em>nom complet</em> de la matière est ce qui est affiché, à la fois dans les titres des pages et dans le menu.</p>
-    <p>La <em>clé</em> est un mot-clé qui sera utilisé uniquement dans l'adresse des pages reliées à la matière. Il vaut mieux que ce soit un mot unique, court et sans majuscule. Par exemple, «&nbsp;maths&nbsp;», «&nbsp;phys&nbsp;»...</p>
-    <p>Vous pouvez aussi définir si un nom d'utilisateur et un mot de passe sont nécessaire pour lire le cahier de texte et les programmes de colles. Les utilisateurs et leur mot de passe sont modifiables <a href="utilisateurs<?php echo str_replace('&amp;','?',$urladmin); ?>">ici</a>. Le réglage équivalent pour les documents se fait répertoire par répertoire.</p>
-    <p>Le cahier de texte, les programmes de colles et les documents sont affichés dans le menu automatiquement, uniquement lorsque des entrées correspondantes ont été faites.</p>
-  </div>
-
-<?php
+affichage(array('id' => 0, 'ordre' => 0, 'cle' => 'cle_de_la_matiere', 'nom' => 'Nom de la matière', 'colles' => '', 'cdt' => ''));
 
 // Affichage des matières existantes
-$resultat = $mysqli->query('SELECT max(ordre) FROM matieres');
-$r = $resultat->fetch_row();
-$max = $r[0];
-$resultat = $mysqli->query('SELECT id, ordre, cle, nom, IF(colles=2,\' checked\',\'\') AS colles, IF(cdt=2,\' checked\',\'\') AS cdt FROM matieres');
+$resultat = $mysqli->query('SELECT id, ordre, cle, nom, MOD(colles,2)+MOD(cdt,2)+docs AS nonvide,
+                            IF(colles>1,\' checked\',\'\') AS colles, IF(cdt>1,\' checked\',\'\') AS cdt
+                            FROM matieres');
+$max = $resultat->num_rows;
 $mysqli->close();
 while ( $r = $resultat->fetch_assoc() )
   affichage($r);
diff -urN cahier-de-prepa2.2.0/pages.php cahier-de-prepa3.0.0/pages.php
--- cahier-de-prepa2.2.0/pages.php	2012-09-13 19:54:37.000000000 +0200
+++ cahier-de-prepa3.0.0/pages.php	2013-08-21 01:21:08.725539702 +0200
@@ -7,16 +7,28 @@
 ///////////////////
 // Modifications //
 ///////////////////
-if ( isset($_REQUEST['id']) )  {
+if ( isset($_REQUEST['id']) && is_numeric($id = $_REQUEST['id']) )  {
   // Connexion à la base de données
   $mysqli = new mysqli($serveur,"$base-adm",$mdp,$base);
   $mysqli->set_charset('utf8');
+  
+  // Vérification que l'identifiant est valide. Défaut : id=0 (nouvelle page)
+  if ( $id )  {
+    $resultat = $mysqli->query("SELECT id, nom, ordre, (SELECT MAX(p.ordre) FROM pages AS p) AS max FROM pages WHERE id = $id");
+    if ( $resultat->num_rows )  {
+      $r = $resultat->fetch_assoc();
+      $resultat->free();
+      $nom = $r['nom'];
+      $ordre = $r['ordre'];
+    }
+    else
+      $id = 0;
+  }
 
-  // Traitement d'un ajout/modification
+  // Ajout ou modification d'une page
   if ( isset($_REQUEST['modifie']) )  {
-    $id = $_REQUEST['id'];
-    // Vérification des données envoyées (page d'accueil : cle et nom "disabled", donc n'existent pas)
-    if ( !strlen($_REQUEST['titre']) || ( !( $id == 0 ) && ( !strlen($_REQUEST['cle']) || !strlen($_REQUEST['nom']) ) ) )
+    // Vérification des données envoyées
+    if ( !strlen($_REQUEST['titre']) || !strlen($_REQUEST['cle']) || !strlen($_REQUEST['nom']) )
       $message = 'Il n\'est pas possible de valider une page avec une clé, un nom ou un titre vide. Pour supprimer une page, il faut cliquer sur Supprimer.';
     else  {
       // Sauvegarde de la table contenant les données
@@ -24,62 +36,47 @@
       // Validation des données envoyées
       $titre = $mysqli->real_escape_string($_REQUEST['titre']);
       $bandeau = $mysqli->real_escape_string($_REQUEST['bandeau']);
+      $cle = $mysqli->real_escape_string($_REQUEST['cle']);
+      $nom = $mysqli->real_escape_string($_REQUEST['nom']);
       $protection = ( isset($_REQUEST['protection']) ) ? 1 : 0;
-      // Si page d'accueil (id=0), seuls titre, bandeau et protection sont modifiables
-      if ( $id === '0' )
-        $message = ( $mysqli->query("UPDATE pages SET titre = '$titre', bandeau = '$bandeau', protection = $protection WHERE id = 0")
-        ) ? 'La page d\'accueil a bien été modifiée.' : 'La page d\'accueil n\'a pas pu être modifiée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'».';
-      else  {
-        $cle = $mysqli->real_escape_string($_REQUEST['cle']);
-        $nom = $mysqli->real_escape_string($_REQUEST['nom']);
-        // Si identifiant numérique non nul, modification d'une page existante
-        if ( $id )
-          $message = ( $mysqli->query("UPDATE pages SET cle = '$cle', nom = '$nom', titre = '$titre',
-                                       bandeau = '$bandeau', protection = $protection WHERE id = $id")
-          ) ? 'La page «&nbsp;'.stripslashes($nom).'&nbsp;» a bien été modifiée.' : 'La page «&nbsp;'.stripslashes($nom).'&nbsp;» n\'a pas pu être modifiée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'».';
-        // Si id = '', ajout d'une nouvelle page
-        else
-          $message = ( $mysqli->query("INSERT INTO pages SET cle = '$cle', nom = '$nom', titre = '$titre',
-                                       bandeau = '$bandeau', protection = $protection, ordre = ((SELECT MAX(p.ordre) FROM pages AS p)+1)")
-          ) ? 'La page «&nbsp'.stripslashes($nom).'&nbsp;» a bien été ajoutée.' : 'La page «&nbsp;'.stripslashes($nom).'&nbsp;» n\'a pas pu être ajoutée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'».';
-      }
+      // Si identifiant numérique non nul, modification d'une page existante
+      if ( $id )
+        $message = ( $mysqli->query("UPDATE pages SET cle = '$cle', nom = '$nom', titre = '$titre',
+                                     bandeau = '$bandeau', protection = $protection WHERE id = $id")
+        ) ? 'La page <em>'.stripslashes($nom).'</em> a bien été modifiée.' : "La page <em>${r['nom']}</em> n'a pas pu être modifiée. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'».';
+      // Si id = 0, ajout d'une nouvelle page
+      else
+        $message = ( $mysqli->query("INSERT INTO pages SET cle = '$cle', nom = '$nom', titre = '$titre',
+                                     bandeau = '$bandeau', protection = $protection, ordre = ((SELECT MAX(p.ordre) FROM pages AS p)+1)")
+                  && $mysqli->query('ALTER TABLE pages ORDER BY ordre')
+        ) ? 'La page «&nbsp'.stripslashes($nom).'</em> a bien été ajoutée.' : 'La page <em>'.stripslashes($nom).'</em> n\'a pas pu être ajoutée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'».';
     }
   }
 
-  // Traitement d'une transformation/suppression (impossible pour la page d'accueil, id=0)
-  elseif ( is_numeric($id = $_REQUEST['id']) && $id )  {
-    
-    // Vérification de l'existence et récupération de l'ordre d'affichage
-    $resultat = $mysqli->query("SELECT ordre, nom, (SELECT MAX(ordre) FROM pages) AS max FROM pages WHERE id = $id");
-    if ( $resultat->num_rows )  {
-      $r = $resultat->fetch_assoc();
-      $ordre = $r['ordre'];
-      $nom = $r['nom'];
-      $resultat->free();
+  elseif ( $id )  {
 
-      // Sauvegarde de la table contenant les données
-      sauvegarde_mysql('pages');
+    // Sauvegarde de la table contenant les données
+    sauvegarde_mysql('pages');
+
+    // Déplacement vers le haut
+    if ( isset($_REQUEST['monte']) && ( $ordre > 1 ) )
+      $message = ( $mysqli->query("UPDATE pages SET ordre = (2*$ordre-1-ordre) WHERE ordre = $ordre OR ordre = ($ordre-1)")
+                && $mysqli->query('ALTER TABLE pages ORDER BY ordre')
+      ) ? "La page <em>$nom</em> a bien été montée d'une place." : "La page <em>$nom</em> n'a pas pu être déplacée. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'».';
+
+    // Déplacement vers le bas
+    elseif ( isset($_REQUEST['descend']) && ( $ordre < $r['max'] ) )
+      $message = ( $mysqli->query("UPDATE pages SET ordre = (2*$ordre+1-ordre) WHERE ordre = $ordre OR ordre = ($ordre+1)")
+                && $mysqli->query('ALTER TABLE pages ORDER BY ordre')
+      ) ? "La page <em>$nom</em> a bien été descendue d'une place." : "La page <em>$nom</em> n'a pas pu être déplacée. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'».';
+
+    // Suppression
+    elseif ( isset($_REQUEST['supprime']) && ( $r['max'] > 1 ) )
+      $message = ( $mysqli->query("DELETE FROM pages WHERE id = $id")
+                && $mysqli->query("DELETE FROM infos WHERE page = $id")
+                && $mysqli->query("UPDATE pages SET ordre = (ordre-1) WHERE ordre > $ordre")
+      ) ? "La page <em>$nom</em> a bien été supprimée, ainsi que les informations qu'elle comportait." : "La page <em>$nom</em> n'a pas pu être supprimée. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'».';
 
-      // Déplacement vers le haut
-      if ( isset($_REQUEST['monte']) && ( $ordre > 2 ) )
-        $message = ( $mysqli->query("UPDATE pages SET ordre = (2*$ordre-1-ordre) WHERE ordre = $ordre OR ordre = ($ordre-1)")
-                  && $mysqli->query('ALTER TABLE pages ORDER BY ordre')
-        ) ? "La page «&nbsp;$nom&nbsp;» a bien été montée d'une place." : "La page «&nbsp;$nom&nbsp;» n'a pas pu être déplacée. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'».';
-      // Déplacement vers le bas
-      elseif ( isset($_REQUEST['descend']) && ( $ordre < $r['max'] ) )
-        $message = ( $mysqli->query("UPDATE pages SET ordre = (2*$ordre+1-ordre) WHERE ordre = $ordre OR ordre = ($ordre+1)")
-                  && $mysqli->query('ALTER TABLE pages ORDER BY ordre')
-        ) ? "La page «&nbsp;$nom&nbsp;» a bien été descendue d'une place." : "La page «&nbsp;$nom&nbsp;» n'a pas pu être déplacée. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'».';
-      // Suppression
-      elseif ( isset($_REQUEST['supprime']) && ( $r['max'] > 1 ) )  {
-        // Sauvegarde de la table contenant les données
-        sauvegarde_mysql('infos');
-        $message = ( $mysqli->query("DELETE FROM pages WHERE id = $id")
-                  && $mysqli->query("DELETE FROM infos WHERE page = $id")
-                  && $mysqli->query("UPDATE pages SET ordre = (ordre-1) WHERE ordre > $ordre")
-        ) ? "La page «&nbsp;$nom&nbsp;» a bien été supprimée, ainsi que les informations qu'elle comportait." : "La page «&nbsp;$nom&nbsp;» n'a pas pu être supprimée. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'».';
-      }
-    }
   }
   $mysqli->close();
 }
@@ -89,34 +86,42 @@
 //////////
 // Haut de page, menu et message
 $p = 'pages';
-$t = 'Modifications des pages d\'informations du site';
+$t = 'Modifications des pages d\'informations';
 $mysqli = new mysqli($serveur,$base,$mdp,$base);
 $mysqli->set_charset('utf8');
 include('haut.php');
 
+?>
+
+  <div class="item aide">
+    <h3>Aide et explications</h3>
+    <p>Vous pouvez ci-dessous ajouter une page d'information ou modifier les pages existantes ou leur ordre d'apparition dans le menu. Supprimer une page supprime automatiquement les informations qui y sont.</p>
+    <h4>Préférences associées à chaque page</h4>
+    <ul>
+      <li>Le <em>titre</em> sera affiché en haut de page et dans la barre de titre du navigateur. Par exemple, «&nbsp;À propos de l'ADS et du TIPE&nbsp;».</li>
+      <li>Le <em>nom dans le menu</em> est affiché dans le menu en tant que lien vers la page. Il est préférable qu'il rentre sur une ligne, il faut donc le choisir assez court. Par exemple, «&nbsp;Informations ADS/TIPE&nbsp;».</li>
+      <li>La <em>clé</em> est un mot-clé qui est utilisé uniquement dans l'adresse de la page (<code>http://<?php echo $site; ?>/?[clé]</code>). Il vaut mieux que ce soit un mot unique, court et sans majuscule. Par exemple, «&nbsp;ads-tipe&nbsp;».</li>
+      <li>Le <em>texte de début</em> sera affiché au-dessus des informations de la page. Il s'agit d'une ou deux phrases maximum.</li>
+    </ul>
+    <p>La case à cocher <em>Demande d'identification pour l'affichage de la page </em> permet de restreindre la visibilité de la page aux visiteurs qui se sont identifiés. Dans ce cas, après <a href="utilisateurs">avoir créé un compte pour les élèves ici</a>, ces informations seront accessibles aux élèves auxquels vous aurez donné l'identifiant et le mot de passe choisis.</p>
+  </div>
+
+<?php
 // Fonction d'affichage
 function affichage($r)  {
-  // Si l'identifiant est 0, c'est la page d'accueil (non déplaçable, non supprimable, clé non modifiable)
-  // Si l'identifiant est '', c'est une nouvelle page 
-  if ( ( $id = $r['id'] ) == '' )  {
-    $debut = 'Créer une nouvelle page d\'informations';
-    $valide1 = "\n    <input class=\"bouton\" type=\"submit\" name=\"modifie\" value=\"Valider\" title=\"Valider les modifications\">";
-    $valide = $monte = $descend = $suppr = $disabled = '';
+  // Si l'identifiant est 0, c'est une nouvelle page 
+  if ( $id  = $r['id'] )  {
+    $debut = "Page n°${r['ordre']}&nbsp;: ${r['nom']}";
+    $valide1 = '';
+    $valide = "\n      <input type=\"submit\" name=\"modifie\" value=\"Valider\" title=\"Valider les modifications\">";
+    $monte = ( $r['ordre'] > 1 ) ? "\n      <input type=\"submit\" name=\"monte\" value=\"&uarr;\" title=\"Remonter la page dans l'ordre d'apparition\">" : '';
+    $descend = ( $r['ordre'] < $GLOBALS['max'] ) ? "\n      <input type=\"submit\" name=\"descend\" value=\"&darr;\" title=\"Descendre la page dans l'ordre d'apparition\">" : '';
+    $suppr = ( $GLOBALS['max'] > 1 ) ? "\n      <input type=\"submit\" name=\"supprime\" value=\"Supprimer\" title=\"Supprimer la page\">" : '';
   }
-  elseif ( $id == 0 )  {
-    $debut = 'Page d\'accueil (page n°1)';
+  else  {
+    $debut = 'Nouvelle page';
     $valide1 = "\n    <input class=\"bouton\" type=\"submit\" name=\"modifie\" value=\"Valider\" title=\"Valider les modifications\">";
     $valide = $monte = $descend = $suppr = '';
-    $disabled = ' disabled';
-  }
-  else  {
-    $debut = "Page d'informations n°${r['ordre']}";
-    $valide1 = '';
-    $valide = "\n      <input type=\"submit\" name=\"modifie\" value=\"Valider\" title=\"Valider les modifications\">";
-    $monte = ( $r['ordre'] == 2 ) ? '' : "\n      <input type=\"submit\" name=\"monte\" value=\"&uarr;\" title=\"Remonter la page dans l'ordre d'apparition\">";
-    $descend = ( $r['ordre'] == $GLOBALS['max'] ) ? '' : "\n      <input type=\"submit\" name=\"descend\" value=\"&darr;\" title=\"Descendre la page dans l'ordre d'apparition\">";
-    $suppr = "\n      <input type=\"submit\" name=\"supprime\" value=\"Supprimer\" title=\"Supprimer la page\">";
-    $disabled = '';
   }
   echo <<<FIN
   <div class="item admin">
@@ -125,11 +130,11 @@
     <p class="boutons">$valide$monte$descend$suppr
     </p>
     <p class="ligne"><label for="titre$id">Titre&nbsp;: </label><input type="text" id="titre$id" name="titre" value="${r['titre']}" size="80"></p>
-    <p class="ligne"><label for="nom$id">Nom dans le menu&nbsp;: </label><input type="text" id="nom$id" name="nom" value="${r['nom']}" size="50"$disabled></p>
-    <p class="ligne"><label for="cle$id">Clé dans l'adresse&nbsp;: </label><input type="text" id="cle$id" name="cle" value="${r['cle']}" size="30"$disabled></p>
+    <p class="ligne"><label for="nom$id">Nom dans le menu&nbsp;: </label><input type="text" id="nom$id" name="nom" value="${r['nom']}" size="50"></p>
+    <p class="ligne"><label for="cle$id">Clé dans l'adresse&nbsp;: </label><input type="text" id="cle$id" name="cle" value="${r['cle']}" size="30"></p>
     <p class="ligne"><label for="bandeau$id">Texte de début&nbsp;:</label></p>
     <textarea id="bandeau$id" name="bandeau" rows="2" cols="100">${r['bandeau']}</textarea>
-    <p class="ligne"><label for="protection$id">Accès avec mot de passe&nbsp;: </label><input type="checkbox" id="protection$id" name="protection" value="1"${r['protection']}></p>
+    <p class="ligne"><label for="protection">Demande d'identification pour l'affichage de la page&nbsp;: </label><input type="checkbox" id="protection" name="protection" value="1"${r['protection']}></p>
     <input type="hidden" name="id" value="$id">
   </form>
   </div>
@@ -139,25 +144,11 @@
 }
 
 // Formulaire vide pour une nouvelle page et aide générale
-affichage(array('id' => '', 'cle' => 'cle_de_la_page', 'nom' => 'Nom de la page', 'titre' => 'Titre de la page', 'bandeau' => 'Texte (éventuel) marqué en haut de la page', 'protection' => ''));
-?>
-
-  <div class="item aide">
-    <p>Vous pouvez ci-dessus ajouter une page d'information, ci-dessous modifier les existantes ou leur ordre. Supprimer une page supprime automatiquement les informations qui y sont.</p>
-    <p>Le <em>titre</em> de la page est ce qui sera affiché en haut de page et dans la barre de titre du navigateur. Par exemple, «&nbsp;À propos de l'ADS et du TIPE&nbsp;».</p>
-    <p>Le <em>nom</em> sera affiché dans le menu en tant que lien vers la page. Il est censé être un peu plus court que le titre. Par exemple, «&nbsp;Informations ADS/TIPE&nbsp;».</p>
-    <p>La <em>clé</em> est un mot-clé qui sera utilisé uniquement dans l'adresse de la page («&nbsp;<code>http://<?php echo $site; ?>/?[clé]</code>&nbsp;»). Il vaut mieux que ce soit un mot unique, court et sans majuscule. Par exemple, «&nbsp;tipe&nbsp;».</p>
-    <p>Le <em>texte de début</em> sera affiché systématiquement avant la liste des informations s'il y en a. Par exemple, «&nbsp;Dernières informations&nbsp;:&nbsp;». Si aucune informations n'est disponible sur la page, il ne sera pas utilisé.</p>
-    <p>Si la case <em>Accès avec mot de passe</em> est cochée, un nom d'utilisateur et un mot de passe seront demandés. Les utilisateurs et leur mot de passe sont modifiables <a href="utilisateurs">ici</a>.</p>
-  </div>
-
-<?php
+affichage(array('id' => 0, 'cle' => 'cle_de_la_page', 'nom' => 'Nom de la page', 'titre' => 'Titre de la page', 'bandeau' => 'Texte (éventuel) marqué en haut de la page', 'protection' => ''));
 
 // Affichage des pages existantes
-$resultat = $mysqli->query('SELECT max(ordre) FROM pages');
-$r = $resultat->fetch_row();
-$max = $r[0];
 $resultat = $mysqli->query('SELECT id,ordre,cle,nom,titre,bandeau,IF(protection=1,\' checked\',\'\') AS protection FROM pages');
+$max = $resultat->num_rows;
 $mysqli->close();
 while ( $r = $resultat->fetch_assoc() )
   affichage($r);
diff -urN cahier-de-prepa2.2.0/prefs.php cahier-de-prepa3.0.0/prefs.php
--- cahier-de-prepa2.2.0/prefs.php	1970-01-01 01:00:00.000000000 +0100
+++ cahier-de-prepa3.0.0/prefs.php	2013-08-21 01:34:46.401565867 +0200
@@ -0,0 +1,258 @@
+<?php
+// Sécurité et récupération des données de configuration
+// Mode admin obligatoire (OK=2)
+define('OK',2);
+include('debut.php');
+
+///////////////////
+// Modifications //
+///////////////////
+if ( isset($_REQUEST['modifie']) )  {
+  // Connexion à la base de données
+  $mysqli = new mysqli($serveur,"$base-adm",$mdp,$base);
+  $mysqli->set_charset('utf8');
+
+  // Traitement de la modification du nom d'utilisateur
+  if ( isset($_REQUEST['modifie_nom']) )  {
+    // Vérification des données envoyées
+    if ( $_SESSION['mdp'] != SHA1($mysqli->real_escape_string($_REQUEST['mdp'])) )
+      $message = 'Votre identifiant n\'a pas pu être modifié&nbsp;: le mot de passe renseigné est incorrect.';
+    elseif ( !strlen($nom = $mysqli->real_escape_string($_REQUEST['nom'])) )
+      $message = 'Votre identifiant n\'a pas pu être modifié&nbsp;: il ne peut pas être vide.';
+    else  {
+      // Sauvegarde de la table contenant les données
+      sauvegarde_mysql('utilisateurs');
+      $message = ( $mysqli->query("UPDATE utilisateurs SET nom = '$nom' WHERE id = ${_SESSION['id']}") ) ? 'Votre identifiant a bien été modifié.' : 'Votre identifiant n\'a pas pu être modifié. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'».';
+      $_SESSION['nom'] = $nom;
+    }
+  }
+
+  // Traitement de la modification du mot de passe
+  elseif ( isset($_REQUEST['modifie_mdp']) )  {
+    // Vérification des données envoyées
+    if ( $_SESSION['mdp'] != SHA1($mysqli->real_escape_string($_REQUEST['mdp0'])) )
+      $message = 'Votre mot de passe n\'a pas pu être modifié&nbsp;: le mot de passe actuel est incorrect.';
+    elseif ( !strlen($mdp1 = $mysqli->real_escape_string($_REQUEST['mdp1'])) )
+      $message = 'Votre mot de passe n\'a pas pu être modifié&nbsp;: il ne peut pas être vide.';
+    elseif ( $_REQUEST['mdp1'] != $_REQUEST['mdp2'] )
+      $message = 'Votre mot de passe n\'a pas pu être modifié&nbsp;: vous avez tapé deux mots de passe différents.';
+    else  {
+      // Sauvegarde de la table contenant les données
+      sauvegarde_mysql('utilisateurs');
+      $message = ( $mysqli->query("UPDATE utilisateurs SET mdp = SHA1('$mdp1') WHERE id = ${_SESSION['id']}") ) ? 'Votre mot de passe a bien été modifié.' : 'Votre mot de passe n\'a pas pu être modifié. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'».';
+      $_SESSION['mdp'] = SHA1($mdp1);
+    }
+  }
+
+  // Traitement de la modification des matières
+  elseif ( isset($_REQUEST['modifie_matieres']) )  {
+    // Validation des données envoyées
+    $m = array_filter(array_unique($_REQUEST['matieres'],SORT_NUMERIC),'is_numeric');
+    if ( empty($m) )
+      $message = 'Vos matières associées n\'ont pas été modifiées&nbsp;: vous devez avoir au moins une matière associée.';
+    else
+      // Sauvegarde de la table contenant les données
+      sauvegarde_mysql('utilisateurs');
+      $m = implode(',',$m);
+      $message = ( $mysqli->query("UPDATE utilisateurs SET matieres = '$m' WHERE id = ${_SESSION['id']}") ) ? 'Vos matières associées ont bien été modifiées.' : 'Vos matières associées n\'ont pas pu être modifiées. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'».';
+      $_SESSION['matieres'] = $m;
+  }
+
+  // Traitement de la modification de protection par défaut des documents
+  elseif ( isset($_REQUEST['modifie_protection']) )  {
+    // Validation des données envoyées
+    $protection = ( isset($_REQUEST['protection']) ) ? 1 : 0;
+    // Sauvegarde de la table contenant les données
+    sauvegarde_mysql('utilisateurs');
+    $message = ( $mysqli->query("UPDATE utilisateurs SET protection = $protection WHERE id = ${_SESSION['id']}") ) ? 'Votre choix par défaut de protection des documents a bien été modifié.' : 'Votre choix par défaut de protection des documents n\'a pas pu être modifié. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'».';
+    $_SESSION['protection'] = $protection;
+  }
+
+  // Traitement de la modification d'une matière
+  elseif ( isset($_REQUEST['modifie_matiere']) && is_numeric($id = $_REQUEST['id']) )  {
+    // Vérification des données envoyées
+    if ( !strlen($_REQUEST['cle']) || !strlen($_REQUEST['nom']) )
+      $message = 'Il n\'est pas possible de valider une matière sans clé ou sans nom.';
+    else  {
+      // Validation des données envoyées
+      $cle = $mysqli->real_escape_string($_REQUEST['cle']);
+      $nom = $mysqli->real_escape_string($_REQUEST['nom']);
+      $colles = ", colles = IF((SELECT id FROM colles WHERE matiere = $id AND cache = 0 LIMIT 1),".( ( isset($_REQUEST['colles']) ) ? '3,2)' : '1,0)');
+      $cdt = ", cdt = IF((SELECT id FROM cdt WHERE matiere = $id AND cache = 0 LIMIT 1),".( ( isset($_REQUEST['cdt']) ) ? '3,2)' : '1,0)');
+      // Sauvegarde des tables contenant les données
+      sauvegarde_mysql('matieres');
+      sauvegarde_mysql('reps');
+      $message = ( $mysqli->query("UPDATE matieres SET cle = '$cle', nom = '$nom'$colles$cdt WHERE id = $id")
+                && $mysqli->query("UPDATE reps SET nom = '$nom' WHERE matiere = $id AND parent = 0")
+      ) ? 'La matière <em>'.stripslashes($nom).'</em> a bien été modifiée.' : 'La matière <em>'.stripslashes($nom).'</em> n\'a pas pu être modifiée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'».';
+    }
+  }
+  $mysqli->close();
+}
+
+//////////////
+//// HTML ////
+//////////////
+// Haut de page, menu et message
+$p = 'prefs';
+$t = 'Préférences';
+$mysqli = new mysqli($serveur,$base,$mdp,$base);
+$mysqli->set_charset('utf8');
+include('haut.php');
+
+/////////////////
+// Identifiant //
+/////////////////
+?>
+
+  <div class="item aide">
+    <h3>Aide et explications</h3>
+    <p>Vous pouvez ci-dessous modifier les réglages qui vous concernent personnellement&nbsp;: votre identifiant et votre mot de passe, les matières apparaissant dans le menu à gauche, le choix par défaut de la protection des documents, ainsi que les réglages de chaque matière précédemment choisies.</p>
+    <h4>Identifiant</h4>
+    <p>Vous pouvez changer d'identifiant. Celui-ci ne sert que pour vous connecter, il n'est pas accessible aux élèves. Les accents ne posent pas de problème. Vous devez renseigner votre mot de passe pour valider cette modification.</p>
+    <h4>Mot de passe</h4>
+    <p>Vous pouvez modifier votre mot de passe. Il faut bien entendu renseigner le mot de passe actuel et taper deux fois le nouveau mot de passe pour éviter les erreurs de frappe.</p>
+    <p>Les mots de passe sont chiffrés avant d'être stockés dans la base de données&nbsp;: il n'y a donc aucun souci sur leur confidentialité. Même l'administrateur du site, l'hébergeur ou d'éventuels pirates ne pourront jamais y avoir accès. Lors de la connexion, le mot de passe entré est chiffré à son tour et ce sont les deux chiffrements qui sont comparés. Attention&nbsp;: il est très dangereux que ce mot de passe soit évident. Un bon mot de passe est un mot de passe d'au moins 8 caractères contenant des lettres, des chiffres et au moins un symbole parmi «&nbsp;?&nbsp;;&nbsp;:&nbsp;!&nbsp;.&nbsp;,-&nbsp;_&nbsp;». Mais vous faîtes ce que vous voulez...</p>
+    <h4>Les matières vous concernant</h4>
+    <p>Vous pouvez choisir quelles matières apparaissent dans le menu de gauche. Ce choix ne modifie pas les matières apparaissant dans le menu de la partie publique, ni leur ordre d'apparition. Plusieurs collègues peuvent afficher la même matière (et donc y ajouter du contenu).</p>
+    <h4>Protection par défaut des documents</h4>
+    <p>Pour chaque document, vous pouvez spécifier s'il est nécessaire au visiteur de s'identifier pour le voir (accès restreint aux élèves, <a href="utilisateurs">compte à créer ici</a>) ou non. Ce choix vous permet simplement de pré-cocher la case si vous le souhaiter. Vous pourrez bien sûr, pour chaque document, restreindre ou non l'accès lors de l'envoi.</p>
+    <h4>Préférences de matière</h4>
+    <p>Pour chaque matière vous concernant (choix fait juste au-dessus), vous pouvez modifier le <em>nom complet</em>, qui s'affiche dans le menu et dans les titres des pages.</p>
+    <p>Vous pouvez aussi modifier la <em>clé dans l'adresse</em>, qui est un mot-clé utilisé uniquement dans l'adresse des pages associées à la matière. Il vaut mieux que ce soit un mot unique, court et sans majuscule. Par exemple, «&nbsp;maths&nbsp;», «&nbsp;phys&nbsp;»...</p>
+    <p>Les deux cases à cocher <em>Demande d'identification pour l'affichage...</em> permettent de restreindre la visibilité du programme de colles ou du cahier de texte aux visiteurs qui se sont identifiés. Dans ce cas, après <a href="utilisateurs">avoir créé un compte pour les élèves ici</a>, ces informations seront accessibles aux élèves auxquels vous aurez donné l'identifiant et le mot de passe choisis.</p>
+    <p>Les liens vers le cahier de texte, les programmes de colles et les documents sont affichés automatiquement dans le menu de la partie publique, uniquement lorsqu'il y a derrière quelque chose à afficher.</p>
+  </div>
+
+  <div class="item admin">
+  <form action="" method="post">
+    <input class="bouton" type="submit" name="modifie_nom" value="Valider" title="Valider les modifications">
+    <h3>Modifier mon identifiant</h3>
+    <p class="ligne"><label for="nom">Identifiant&nbsp;: </label><input type="text" id="nom" name="nom" value="<?php echo $_SESSION['nom']; ?>"></p>
+    <p class="ligne"><label for="mdp">Mot de passe&nbsp;: </label><input type="password" id="mdp" name="mdp" value=""></p>
+    <input type="hidden" name="modifie" value="">
+  </form>
+  </div>
+<?php
+
+//////////////////
+// Mot de passe //
+//////////////////
+?>
+
+  <div class="item admin">
+  <form action="" method="post">
+    <input class="bouton" type="submit" name="modifie_mdp" value="Valider" title="Valider les modifications">
+    <h3>Modifier mon mot de passe</h3>
+    <p class="ligne"><label for="mdp0">Mot de passe actuel&nbsp;: </label><input type="password" id="mdp0" name="mdp0" value=""></p>
+    <p class="ligne"><label for="mdp1">Nouveau&nbsp;: </label><input type="password" id="mdp1" name="mdp1" value=""></p>
+    <p class="ligne"><label for="mdp2">Confirmation&nbsp;: </label><input type="password" id="mdp2" name="mdp2" value=""></p>
+    <input type="hidden" name="modifie" value="">
+  </form>
+  </div>
+<?php
+
+///////////////////////////////////////////
+// Détermination des matières concernées //
+///////////////////////////////////////////
+// Récupération de toutes les matières
+$resultat = $mysqli->query("SELECT id, nom, ordre FROM matieres ORDER BY ordre");
+$select = "    <p id=\"ligneXX\" class=\"ligne\">Matière n°XX&nbsp;:\n      <select name=matieres[XX]>\n";
+$max = $resultat->num_rows;
+while ( $r = $resultat->fetch_assoc() )
+  $select .= "        <option value=\"${r['id']}\">${r['nom']}</option>\n";
+$resultat->free();
+$select .= "      </select>\n    </p>\n";
+?>
+
+  <div class="item admin">
+  <form action="" method="post">
+    <input class="bouton" type="submit" name="modifie_matieres" value="Valider" title="Valider les modifications">
+    <h3>Modifier mes matières</h3>
+<?php
+$n = 1;
+foreach ( $matieres as $m )
+  echo str_replace("\"${m['id']}\"","\"${m['id']}\" selected",str_replace('XX',$n++,$select));
+?>
+    <p class="boutons">
+      <input type="button" id="ajouter" value="Ajouter une matière">
+      <input type="button" id="retirer" value="Retirer la dernière matière">
+    </p>
+    <input type="hidden" name="modifie" value="">
+  </form>
+  </div>
+<?php
+
+//////////////////////////////////////////////////
+// Choix de protection par défaut des documents //
+//////////////////////////////////////////////////
+$protection = ( $_SESSION['protection'] ) ? ' checked' : '';
+?>
+
+  <div class="item admin">
+  <form action="" method="post">
+    <input class="bouton" type="submit" name="modifie_protection" value="Valider" title="Valider les modifications">
+    <h3>Modifier la protection par défaut de mes documents</h3>
+    <p class="ligne"><label for="protection">Cocher automatiquement la case de protection pour les nouveaux documents&nbsp;: </label><input type="checkbox" id="protection" name="protection" value="1"<?php echo $protection; ?>></p>
+    <input type="hidden" name="modifie" value="">
+  </form>
+  </div>
+<?php
+
+/////////////////////////////////////////
+// Préférences des matières concernées //
+/////////////////////////////////////////
+foreach ( $matieres as $m )  {
+  $colles = ( $m['colles'] > 1 ) ? ' checked': '';
+  $cdt = ( $m['cdt'] > 1 ) ? ' checked': '';
+  $id = $m['id'];
+  echo <<<FIN
+
+  <div class="item admin">
+  <form action="" method="post">
+    <input class="bouton" type="submit" name="modifie_matiere" value="Valider" title="Valider les modifications">
+    <h3>Modifier la matière <em>${m['nom']}</em></h3>
+    <p class="ligne"><label for="nom$id">Nom complet&nbsp;: </label><input type="text" id="nom$id" name="nom" value="${m['nom']}" size="50"></p>
+    <p class="ligne"><label for="cle$id">Clé dans l'adresse&nbsp;: </label><input type="text" id="cle$id" name="cle" value="${m['cle']}" size="30"></p>
+    <p class="ligne"><label for="colles$id">Demande d'identification pour l'affichage du programme de colle</label><input type="checkbox" id="colles$id" name="colles" value="1"$colles></p>
+    <p class="ligne"><label for="cdt$id">Demande d'identification pour l'affichage du cahier de texte</label><input type="checkbox" id="cdt$id" name="cdt" value="1"$cdt></p>
+    <input type="hidden" name="id" value="$id">
+    <input type="hidden" name="modifie" value="">
+  </form>
+  </div>
+
+FIN;
+}
+?>
+
+  <script type="text/javascript">
+$( function() {
+  
+  var n = <?php echo $n; ?>;
+  $('#ajouter').click(function () {
+    if ( n <= <?php echo $max; ?> )  {
+      $('#ligne'+(n-1)).clone(true).attr('id','ligne'+n).insertAfter($('#ligne'+(n-1)));
+      $('#ligne'+n).find('select[name="matieres['+(n-1)+']"]').attr('name','matieres['+n+']');
+      $('#ligne'+n).html($('#ligne'+n).html().replace('n°'+(n-1),'n°'+n))
+      $('select[name="matieres['+n+']"]').val([1]);
+      n++;
+    }
+  });
+  $('#retirer').click(function () {
+    if ( n > 2 )  {
+      $('#ligne'+(n-1)).remove();
+      n--;
+    }
+  });
+
+});
+  </script>
+
+  <script type="text/javascript" src="textarea.js.php"></script>
+
+<?php
+$mysqli->close();
+// Bas de page
+include('bas.php');
+exit();
+?>
diff -urN cahier-de-prepa2.2.0/README.php cahier-de-prepa3.0.0/README.php
--- cahier-de-prepa2.2.0/README.php	2012-08-15 11:52:59.000000000 +0200
+++ cahier-de-prepa3.0.0/README.php	2013-08-23 18:36:16.365056775 +0200
@@ -9,7 +9,7 @@
 
 Plus d'informations sont disponibles sur <http://cahier-de-prepa.fr>.
 
-Copyright Cyril Ravat, <contact@cahier-de-prepa.fr> (2009-2011) 
+Copyright Cyril Ravat, <contact@cahier-de-prepa.fr> (2009-2013) 
 
 Ce logiciel est régi par la licence CeCILL soumise au droit français et
 respectant les principes de diffusion des logiciels libres. Vous pouvez
diff -urN cahier-de-prepa2.2.0/rss.php cahier-de-prepa3.0.0/rss.php
--- cahier-de-prepa2.2.0/rss.php	1970-01-01 01:00:00.000000000 +0100
+++ cahier-de-prepa3.0.0/rss.php	2013-08-23 17:48:29.200965025 +0200
@@ -0,0 +1,35 @@
+<?php
+// Sécurité et récupération des données de configuration
+define('OK',1);
+include('debut.php');
+
+// Ce script sert simplement à donner le lien du flux RSS.
+// S'il existe un utilisateur de type élève, qui sert à demander une identification
+// avant d'afficher certains contenus, on suppose qu'il est nécessaire de se
+// connecter pour voir le lien.
+
+$p = '';
+$t = "La $classe du lycée $lycee - Flux RSS";
+
+$mysqli = premiere_connexion();
+if ( !$lecteur )  {
+  $resultat = $mysqli->query('SELECT id FROM utilisateurs WHERE matieres = \'0\'');
+  if ( $resultat->num_rows )  {
+    $resultat->free();
+    // Demander l'identification
+    include('login_lecture.php');
+  }
+}
+
+// Haut de page, menu et message
+include('haut.php');
+  
+?>
+
+  <h3>Le flux RSS est disponible à l'adresse</h3>
+  <p><a href="documents/<?php echo sha1($GLOBALS['base']); ?>/rss.xml">http://<?php echo "$site/documents/".sha1($GLOBALS['base']).'/rss.xml'; ?></a></p>
+
+<?php
+// Bas de page
+include('bas.php');
+?>
diff -urN cahier-de-prepa2.2.0/semaines.php cahier-de-prepa3.0.0/semaines.php
--- cahier-de-prepa2.2.0/semaines.php	2012-09-01 17:45:11.000000000 +0200
+++ cahier-de-prepa3.0.0/semaines.php	2013-08-22 17:21:01.054147474 +0200
@@ -7,6 +7,19 @@
 ///////////////////
 // Modifications //
 ///////////////////
+if ( isset($_REQUEST['initialise']) && in_array($_REQUEST['zone'],array('A','B','C')) )  {
+  // Sauvegarde de la table contenant les données
+  sauvegarde_mysql('semaines');
+  // Récupération des semaines
+  include('definition_semaines.php');
+  // Écriture MySQL
+  $mysqli = new mysqli($serveur,"$base-adm",$mdp,$base);
+  $mysqli->set_charset('utf8');
+  $mysqli->query('TRUNCATE semaines');
+  $message = ( $mysqli->query($semaines) ) ? 'Les semaines ont bien été réinitialisées' : 'Les semaines n\'ont pas pu être réinitialisées. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'».';
+  $mysqli->close();
+}
+
 if ( isset($_REQUEST['modifie']) && !empty($_REQUEST['debut']) )  {
   // Sauvegarde de la table contenant les données
   sauvegarde_mysql('semaines');
@@ -82,12 +95,25 @@
 ?>
   
   <div class="item aide">
+    <h3>Aide et explications</h3>
     <p>Vous pouvez ci-dessous modifier le planning annuel, c'est-à-dire l'ensemble des semaines et vacances ainsi que s'il des colles sont prévues ou non cette semaine-là. Ce planning annuel est utilisé pour les programmes de colles et pour les cahiers de texte.</p>
+    <p>Une réinitialisation complète est possible, en fonction de la zone scolaire. Il s'agit d'un planning allant jusqu'à juillet.</p>
     <p>La colonne <em>Colles</em> est à cocher si des colles sont prévues cette semaine-là.</p>
     <p>La validation des données se fait de façon globale, par les boutons de validation en haut ou en bas du formulaire&nbsp;: toutes les modifications sont prises en compte en une seule fois. De plus, les semaines sont automatiquement réorganisées par ordre chronologique&nbsp;: pas besoin de tout effacer et tout réécrire pour insérer une semaine oubliée, il suffit de l'ajouter en bas.</p>
     <p>Pour supprimer une semaine, il suffit de vider la date et de valider le tableau.</p>
   </div>
 
+  <div class="item admin">
+  <form action="" method="post">
+    <input class="bouton" type="submit" name="initialise" value="Valider" title="Valider les modifications">
+    <h3>Réinitialiser avec les valeurs de 2013-2014</h3>
+    <p class="ligne"><label for="zone">Zone&nbsp;:</label>
+      <select id="zone" name="zone"><option value="A">A</option><option value="B">B</option><option value="C">C</option></select>
+    </p>
+    <p>Attention, si des données du programme de colles sont entrées, le résultat peut être aléatoire...</p>
+  </form>
+  </div>
+
   <div class="item">
     <form action="" method="post">
       <p class="boutons">
diff -urN cahier-de-prepa2.2.0/utilisateurs.php cahier-de-prepa3.0.0/utilisateurs.php
--- cahier-de-prepa2.2.0/utilisateurs.php	2012-12-26 20:06:10.000000000 +0100
+++ cahier-de-prepa3.0.0/utilisateurs.php	2013-08-24 01:17:05.361826346 +0200
@@ -7,101 +7,68 @@
 ///////////////////
 // Modifications //
 ///////////////////
-if ( isset($_REQUEST['id']) )  {
+if ( isset($_REQUEST['nom']) )  {
   // Connexion à la base de données
   $mysqli = new mysqli($serveur,"$base-adm",$mdp,$base);
   $mysqli->set_charset('utf8');
 
-  // Récupération des utilisateurs
-  $resultat = $mysqli->query('SELECT id, nom FROM utilisateurs');
-  while ( $r = $resultat->fetch_assoc() )
-    $utilisateurs[$r['id']] = $r['nom'];
-  $resultat->free();
-
-  // Traitement d'un ajout
-  if ( isset($_REQUEST['ajoute']) && ( strlen($nom = $_REQUEST['nom']) ) && ( $_REQUEST['mdp1'] == $_REQUEST['mdp2'] ) )  {
-    // Vérification des données envoyées
-    if ( !strlen($mdp1 = $_REQUEST['mdp1']) )
-      $message = "L'utilisateur «&nbsp;$nom&nbsp;» n'a pas pu être enregistré, car son mot de passe ne peut pas être vide.";
-    elseif ( in_array($nom,$utilisateurs) )
-      $message = "L'utilisateur «&nbsp;$nom&nbsp;» n'a pas pu être enregistré car un autre utilisateur porte déjà ce nom.";
-    else  {
-      // Sauvegarde de la table contenant les données
-      sauvegarde_mysql('utilisateurs');
-      // Validation des données envoyées
-      $mdp1 = $mysqli->real_escape_string($mdp1);
-      $nom = $mysqli->real_escape_string($nom);
-      if ( !is_numeric($matiere = $_REQUEST['matiere']) )  $matiere = 0;
-      // Envoi MySQL
-      $message = ( $mysqli->query("INSERT INTO utilisateurs SET nom = '$nom', matiere = ( SELECT SUM(id*IF(id=$matiere,1,0)) FROM matieres ), mdp = SHA1('$mdp1')")
-      ) ? 'L\'utilisateur «&nbsp;'.stripslashes($nom).'&nbsp;» a bien été enregistré.' : 'L\'utilisateur «&nbsp;'.stripslashes($nom).'&nbsp;» n\'a pas pu être enregistré. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'».';
+  // Sauvegarde de la table contenant les données
+  sauvegarde_mysql('utilisateurs');
+
+  // Validation de la matière et de l'identifiant éventuels
+  $matiere = $id = 0;
+  if ( isset($_REQUEST['matiere']) && is_numeric($_REQUEST['matiere']) )  {
+    $resultat = $mysqli->query("SELECT id FROM matieres WHERE id = ${_REQUEST['matiere']}");
+    if ( $resultat->num_rows )  {
+      $matiere = $_REQUEST['matiere'];
+      $resultat->free();
     }
   }
-  
-  // Traitement d'une modification de mot de passe
-  elseif ( isset($_REQUEST['modifie_mdp']) && isset($utilisateurs[$id = $_REQUEST['id']]) && ( $_REQUEST['mdp1'] == $_REQUEST['mdp2'] ) )  {
-    // Vérification des données envoyées
-    if ( !strlen($mdp1 = $_REQUEST['mdp1']) )
-      $message = "Le mot de passe de l'utilisateur «&nbsp;${utilisateurs[$id]}&nbsp;» n'a pas pu être modifié, car il ne peut pas être vide.";
-    else  {
-      // Sauvegarde de la table contenant les données
-      sauvegarde_mysql('utilisateurs');
-      // Validation des données envoyées
-      $mdp0 = $mysqli->real_escape_string($_REQUEST['mdp0']);
-      $mdp1 = $mysqli->real_escape_string($mdp1);
-      if ( $mysqli->query("UPDATE utilisateurs SET mdp = SHA1('$mdp1') WHERE id = $id AND mdp = SHA1('$mdp0')") )
-        $message = ( $mysqli->affected_rows ) ? "Le mot de passe de l'utilisateur «&nbsp;${utilisateurs[$id]}&nbsp;» a bien été modifié." : "Le mot de passe de l'utilisateur «&nbsp;${utilisateurs[$id]}&nbsp;» n'a pas pu être modifié car sa valeur actuelle est incorrecte.";
-      else
-        $message = "Le mot de passe de l'utilisateur «&nbsp;${utilisateurs[$id]}&nbsp;» n'a pas pu être modifié. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'».';
+  if ( isset($_REQUEST['id']) && is_numeric($_REQUEST['id']) )  {
+    // type = 0 si professeur, 1 si élève
+    $resultat = $mysqli->query("SELECT nom, mdp, !matieres AS type FROM utilisateurs WHERE id = ${_REQUEST['id']}");
+    if ( $resultat->num_rows )  {
+      $id = $_REQUEST['id'];
+      $r = $resultat->fetch_assoc();
+      $resultat->free();
     }
   }
-
-  // Traitement d'une modification de nom ou de matière
-  elseif ( isset($_REQUEST['modifie']) && isset($utilisateurs[$id = $_REQUEST['id']]) )  {
-    // Vérification des données envoyées
-    if ( !strlen($nom = $_REQUEST['nom']) )
-      $message = 'Il n\'est pas possible de valider un utilisateur sans nom. Pour supprimer un utilisateur, il faut cliquer sur Supprimer.';
-    elseif ( ( $nom != $utilisateurs[$id] ) && in_array($nom,$utilisateurs) )
-      $message = "Les données de l'utilisateur «&nbsp;${utilisateurs[$id]}&nbsp;» n'ont pas pu être modifiées car un autre utilisateur porte déjà ce nom.";
+  
+  // Suppression
+  if ( ( $id ) && isset($_REQUEST['supprime']) )
+    $message = ( $mysqli->query("DELETE FROM utilisateurs WHERE id = $id")
+    ) ? "L'utilisateur <em>${r['nom']}</em> a bien été supprimé." : "L'utilisateur <em>${r['nom']}</em> n'a pas pu être supprimé. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'».';
+  // Modification ou ajout
+  elseif ( strlen($nom = $_REQUEST['nom']) && strlen($mdp1 = $_REQUEST['mdp1']) )  {
+    $n = ( $id ) ? $r['nom'] : $nom;
+    // Vérification que le nouveau nom n'existe pas
+    $nom = $mysqli->real_escape_string($nom);
+    $resultat = $mysqli->query("SELECT id FROM utilisateurs WHERE nom = '$nom' AND id != $id");
+    if ( $resultat->num_rows )  {
+      $message = "L'utilisateur <em>$n</em> n'a pas pu être enregistré&nbsp;: un autre utilisateur porte déjà le nom demandé.";
+      $resultat->free();
+    }
+    // Vérification de la concordance des mots de passe
+    elseif ( $mdp1 != $_REQUEST['mdp2'] )
+      $message = "L'utilisateur <em>$n</em> n'a pas pu être enregistré&nbsp;: les deux mots de passe donnés sont différents.";
     else  {
-      // Sauvegarde de la table contenant les données
-      sauvegarde_mysql('utilisateurs');
-      // Validation des données envoyées
-      $nom = $mysqli->real_escape_string($nom);
-      if ( !is_numeric($matiere = $_REQUEST['matiere']) )  $matiere = 0;
-      // Impossible de supprimer la matière de son propre utilisateur
-      if ( !$matiere && ( $utilisateurs[$id] == $_SESSION['user'] ) )
-        $message = 'Il est impossible de supprimer la matière associé à votre compte, car cela le transformerait en simple lecteur des pages protégées sur la partie publique&nbsp;: vous n\'auriez plus accès à cette interface d\'administration. demander à un collègue de faire cela si besoin.'
-      // Envoi MySQL
-      elseif ( $mysqli->query("UPDATE utilisateurs SET nom = '$nom', matiere = ( SELECT SUM(id*IF(id=$matiere,1,0)) FROM matieres ) WHERE id = $id") )  {
-        // Si on modifie son propre utilisateur, modification des données de session
-        if ( $utilisateurs[$id] == $_SESSION['user'] )  {
-          $SESSION['user'] = $nom;
-          $resultat = $mysqli->query("SELECT cle FROM matieres WHERE id=$matiere");
-          if ( $resultat->num_rows )  {
-            $r = $resultat->fetch_row();
-            $_SESSION['mat'] = $r[0];
-          }
-          else  $matiere = 0;
-          $message = "Les données de l'utilisateur «&nbsp;${utilisateurs[$id]}&nbsp;» ont bien été modifiées.";
-        }
+      $mdp1 = $mysqli->real_escape_string($mdp1);
+      // Modification
+      if ( $id )  {
+        // Vérification du mot de passe actuel (seulement pour les utilisateurs de type professeur)
+        if ( ( !$r['type'] ) && ( sha1($_REQUEST['mdp0']) != $r['mdp'] ) )
+          $message = "L'utilisateur <em>$n</em> n'a pas pu être enregistré&nbsp;: le mot de passe actuel est incorrect.";
         else
-          $message = 'Les données votre compte utilisateur ont bien été modifiées.';
+          $message = ( $mysqli->query("UPDATE utilisateurs SET nom = '$nom', mdp = SHA1('$mdp1') WHERE id = $id")
+          ) ? "Les données de l'utilisateur <em>$n</em> ont bien été modifiées." : "Les données de l\'utilisateur <em>$n</em> n'ont pas pu être modifiées. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'».';
       }
+      // Ajout
       else
-        $message = "Les données de l'utilisateur «&nbsp;${utilisateurs[$id]}&nbsp;» n'ont pas pu être modifiées. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'».';
+        $message = ( $mysqli->query("INSERT INTO utilisateurs SET nom = '$nom', mdp = SHA1('$mdp1'), matieres = '$matiere', protection = 0")
+        ) ? "L'utilisateur <em>$n</em> a bien été enregistré." : "L'utilisateur <em>$n</em> n'a pas pu être enregistré. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'».';
     }
   }
-
-  // Traitement d'une suppression
-  elseif ( isset($_REQUEST['supprime']) && isset($utilisateurs[$id = $_REQUEST['id']]) ) {
-    if ( count($utilisateurs) == 1 )
-      $message = "L'utilisateur «&nbsp;${utilisateurs[$id]}&nbsp;» n'a pas pu être supprimé car il est le seul utilisateur actuellement.";
-    else
-      $message = ( $mysqli->query("DELETE FROM utilisateurs WHERE id = $id")
-      ) ? "L'utilisateur «&nbsp;${utilisateurs[$id]}&nbsp;» a bien été supprimé." : "L'utilisateur «&nbsp;${utilisateurs[$id]}&nbsp;» n'a pas pu être supprimé. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'».';
-  }
-
+  
   $mysqli->close();
 }
 
@@ -117,100 +84,109 @@
 
 // Récupération des matières
 $resultat = $mysqli->query('SELECT id, nom FROM matieres');
-$select_matieres = '<select id="matiere" name="matiere">';
+$select_matieres = '';
 while ( $r = $resultat->fetch_assoc() )
-  $select_matieres .= "<option value=\"${r['id']}\">${r['nom']}</option>";
-$resultat->free();
-$select_matieres .= '<option value="0">Accès aux pages publiques protégées</option></select>';
-
-// Récupération des utilisateurs (et fabrication du select pour l'affichage)
-$resultat = $mysqli->query('SELECT id, nom, matiere FROM utilisateurs ORDER BY IF(matiere=0,100,matiere),nom');
-$mysqli->close();
-$select_utilisateurs = '<select id="id" name="id">';
-unset($utilisateurs);
-while ( $r = $resultat->fetch_assoc() ) {
-  $select_utilisateurs .= "<option value=\"${r['id']}\">${r['nom']}</option>";
-  $utilisateurs[$r['id']] = $r['nom'];
-  $u_mat[$r['id']] = $r['matiere'];
-}
+  $select_matieres .= "        <option value=\"${r['id']}\">${r['nom']}</option>\n";
 $resultat->free();
-$select_utilisateurs .= '</select>';
 
-// Formulaire vide pour nouvel utilisateur et aide générale
+// Aide générale et formulaires
 ?>
 
+  <div class="item aide">
+    <h3>Aide et explications</h3>
+    <p>Vous pouvez ci-dessous ajouter un utilisateur, et modifier les utilisateurs existants. Les utilisateurs sont séparés en deux types&nbsp;:</p>
+    <ul>
+      <li><em>professeur</em>, qui est associé à une matière et permet d'accéder à l'interface d'administration</li>
+      <li><em>élève</em>, qui permet uniquement de visualiser les pages et documents qui seraient protégés par une <em>demande d'identification pour visualiser...</em>. Ce choix est à régler individuellement sur chaque page d'informations, individuellement pour chaque répertoire/documents, et dans vos <a href="prefs">préférences</a> pour les programmes de colles/cahier de texte de vos matières associées.</li>
+    </ul>
+    <p>Une même matière peut être associée à plusieurs utilisateurs de type professeur et chaque professeur peut avoir éventuellement plusieurs matières associées. Ce choix s'effectue dans vos <a href="prefs">préférences</a>.</p>
+    <p>L'identifiant ne sert que pour se connecter, il n'est pas accessible aux élèves. Les accents ne posent pas de problème. Un prénom est une bonne idée d'identifiant pour les utilisateurs de type professeur.</p>
+    <p>Les mots de passe sont chiffrés avant d'être stockés dans la base de données&nbsp;: il n'y a donc aucun souci sur leur confidentialité. Même l'administrateur du site, l'hébergeur ou d'éventuels pirates ne pourront jamais y avoir accès. Lors de la connexion, le mot de passe entré est chiffré à son tour et ce sont les deux chiffrements qui sont comparés. Attention&nbsp;: il est très dangereux que ce mot de passe soit évident. Un bon mot de passe est un mot de passe d'au moins 8 caractères contenant des lettres, des chiffres et au moins un symbole parmi «&nbsp;?&nbsp;;&nbsp;:&nbsp;!&nbsp;.&nbsp;,-&nbsp;_&nbsp;». Mais vous faîtes ce que vous voulez...</p>
+    <p>Pour toutes les modifications d'identifiant ou de mot de passe d'utilisateurs de type professeur, le mot de passe actuel est nécessaire. Il n'est pas nécessaire par contre pour la suppression, afin que le nettoyage des utilisateurs inactifs reste possible depuis l'interface d'administration.</p>
+    <p>Ajouter ou supprimer des utilisateurs ne modifie jamais la liste des matières. Pour ajouter ou supprimer des matières, il faut aller sur <a href="matieres">la page correspondante</a>.</p>
+  </div>
+
   <div class="item admin">
-  <form action="" method="post">
+  <form action="" method="post" id="ajoute_professeur">
     <input class="bouton" type="submit" name="ajoute" value="Valider" title="Valider les modifications">
-    <h3>Créer un nouvel utilisateur</h3>
+    <h3>Créer un nouvel utilisateur (professeur)</h3>
     <p class="ligne"><label for="nom">Nom&nbsp;: </label><input type="text" id="nom" name="nom" value="" size="50"></p>
-    <p class="ligne"><label for="mdp1_1">Mot de passe&nbsp;: </label><input type="password" id="mdp1_1" name="mdp1" value=""></p>
-    <p class="ligne"><label for="mdp2_1">Confirmation&nbsp;: </label><input type="password" id="mdp2_1" name="mdp2" value=""></p>
-    <p class="ligne"><label for="matiere">Matière ou accès&nbsp;: </label>
-      <?php echo $select_matieres; ?>
-
+    <p class="ligne"><label for="mdp1_p">Mot de passe&nbsp;: </label><input type="password" id="mdp1_p" name="mdp1" value=""></p>
+    <p class="ligne"><label for="mdp2_p">Confirmation&nbsp;: </label><input type="password" id="mdp2_p" name="mdp2" value=""></p>
+    <p class="ligne"><label for="matiere">Matière&nbsp;: </label>
+      <select  id="matiere" name="matiere">
+<?php echo $select_matieres; ?>
+      </select>
     </p>
-    <input type="hidden" name="id" value="">
+    <p>La(les) matière(s) associée(s) à cet utilisateur seront modifiables par lui-même une fois qu'il se sera connecté, dans ses <em>préférences</em>. Il est possible de créer une matière <a href="matieres">ici</a>.</p>
   </form>
   </div>
 
-  <div class="item aide">
-    <p>Vous pouvez ci-dessus ajouter un utilisateur, ci-dessous modifier les utilisateurs existants. Supprimer un utilisateur ne supprime pas la matière qui lui est associée.</p>
-    <p>L'accès à l'interface d'administration n'est possible que pour les utilisateurs associés à une matière, assumés être des professeurs. Plusieurs utilisateurs (professeurs) peuvent être associés à une même matière.</p>
-    <p>Le choix <em>Accès aux pages publiques protégées</em> est prévu pour donner la possibilité de voir des contenus protégés. Vous pouvez, au choix, créer un utilisateur par élève ou un pour toute la classe. Cet utilisateur n'a bien sûr pas accès à l'interface d'administration.</p>
-    <p>La protection par mot de passe des répertoires/documents et des pages d'information est réglable pour chaque répertoire/document et chaque page. Pour les programmes de colles et les cahiers de texte, cela peut être modifié <a href="matieres">ici</a>.</p>
-    <p>Les mots de passe sont chiffrés avant d'être stockés dans la base de données&nbsp;: il n'y a donc aucun souci sur leur confidentialité. Même l'administrateur du site, l'hébergeur ou d'éventuels pirates ne pourront jamais y avoir accès. Lors de la connexion, le mot de passe entré est chiffré à son tour et ce sont les deux chiffrements qui sont comparés. Attention&nbsp;: il est très dangereux que ce mot de passe soit évident. Un bon mot de passe est un mot de passe d'au moins 8 caractères contenant des lettres, des chiffres et au moins un symbole parmi «&nbsp;?&nbsp;;&nbsp;:&nbsp;!&nbsp;.&nbsp;,-&nbsp;_&nbsp;».</p>
-    <p>Le nom n'est jamais affiché sur la partie publique du site, il est donc possible de mettre un prénom ou ce que l'on veut. Les accents ne posent pas de problème.</p>
-  </div>
+  <script type="text/javascript">
+$( function() {
+  // Ajout d'un générateur de mot de passe
+  $('#ajoute_professeur').append('<p><input type="button" value="Générer un mot de passe" id="generer_mdp">&nbsp;<span></span></p>');
+  $('#generer_mdp').click( function () {
+    var c = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789,;:!?.';
+    var p = '';
+    for ( var i = 0 ; i < 8 ; i++ )
+      p += c.charAt(1 + Math.floor(Math.random()*68));
+    $(this).next('span').html("Valeur recopiée&nbsp;:&nbsp;<code>"+p+"</code>");
+    $('#mdp1_p,#mdp2_p').val(p);
+  });
+});
+  </script>
 
   <div class="item admin">
   <form action="" method="post">
-    <input class="bouton" type="submit" name="modifie_mdp" value="Valider" title="Valider les modifications">
-    <h3>Modification d'un mot de passe</h3>
-    <p class="ligne"><label for="id">Utilisateur&nbsp;:</label>
-      <?php echo $select_utilisateurs; ?>
-
-    </p>
-    <p class="ligne"><label for="mdp0_2">Mot de passe actuel&nbsp;: </label><input type="password" id="mdp0_2" name="mdp0" value=""></p>
-    <p class="ligne"><label for="mdp1_2">Nouveau&nbsp;: </label><input type="password" id="mdp1_2" name="mdp1" value=""></p>
-    <p class="ligne"><label for="mdp2_2">Confirmation&nbsp;: </label><input type="password" id="mdp2_2" name="mdp2" value=""></p>
+    <input class="bouton" type="submit" name="ajoute" value="Valider" title="Valider les modifications">
+    <h3>Créer un nouvel utilisateur (élèves)</h3>
+    <p class="ligne"><label for="nom">Nom&nbsp;: </label><input type="text" id="nom" name="nom" value="" size="50"></p>
+    <p class="ligne"><label for="mdp1_e">Mot de passe&nbsp;: </label><input type="password" id="mdp1_e" name="mdp1" value=""></p>
+    <p class="ligne"><label for="mdp2_e">Confirmation&nbsp;: </label><input type="password" id="mdp2_e" name="mdp2" value=""></p>
   </form>
   </div>
 
-  <div class='item'>
-    <table id="utilisateurs" class="admin">
-      <thead>
-        <tr>
-          <th>Utilisateurs</th>
-          <th>Matière ou accès aux pages protégées</th>
-          <th></th>
-        </tr>
-      </thead>
-      <tbody>
-  
 <?php
-// Remplissage du tableau permettant de modifier les noms et matières
-$suppr = ( count($utilisateurs) == 1 ) ? '' : ' <input type="submit" name="supprime" value="Supprimer">';
-foreach ( $utilisateurs as $id => $nom )  {
-  $m = str_replace("\"${u_mat[$id]}\"","\"${u_mat[$id]}\" selected",$select_matieres);
+// Récupération des utilisateurs (et fabrication du select pour l'affichage)
+$resultat = $mysqli->query('SELECT u.nom, GROUP_CONCAT(m.nom SEPARATOR \'</em>, <em>\') AS mats FROM utilisateurs AS u JOIN matieres AS m ON FIND_IN_SET(m.id,u.matieres) WHERE u.matieres GROUP BY u.nom ORDER BY u.nom');
+$utilisateurs = '';
+while ( $r = $resultat->fetch_assoc() )
+  $utilisateurs .= "    <li><em>${r['nom']}</em>, associé aux matières <em>${r['mats']}</em></li>\n";
+$resultat->free();
+
+// Récupération des utilisateurs
+
+$resultat = $mysqli->query('SELECT u.id, u.nom, GROUP_CONCAT(m.nom SEPARATOR \'</em>, <em>\') AS mats FROM utilisateurs AS u LEFT JOIN matieres AS m ON FIND_IN_SET(m.id,u.matieres) GROUP BY u.nom ORDER BY !u.matieres,u.nom,m.id');
+while ( $r = $resultat->fetch_assoc() )  {
+  $id = $r['id'];
+  if ( $r['mats'] )  {
+    $r['mats'] = "\n    <p>Utilisateur de type professeur, associé aux matières <em>${r['mats']}</em></p>";
+    $mdp = "\n    <p class=\"ligne\"><label for=\"mdp0_$id\">Mot de passe actuel&nbsp;: </label><input type=\"password\" id=\"mdp0_$id\" name=\"mdp0\" value=\"\"></p>";
+  }
+  else  {
+    $r['mats'] = "\n    <p>Utilisateur de type élève</p>";
+    $mdp = '';
+  }
   echo <<<FIN
-        <tr>
-          <form action="" method="post">
-          <td><input type="text" name="nom" value="$nom" size="20"></td>
-          <td>$m</td>
-          <td><input type="submit" name="modifie" value="Valider">$suppr</td>
-          <input type="hidden" name="id" value="$id">
-          </form>
-        </tr>
+  <div class="item admin">
+  <form action="" method="post">
+    <input class="bouton" type="submit" name="modifie" value="Valider" title="Valider les modifications">
+    <input class="bouton" type="submit" name="supprime" value="Supprimer" title="Supprimer l'utilisateur">
+    <h3>Modifier l'utilisateur <em>${r['nom']}</em></h3>${r['mats']}
+    <p class="ligne"><label for="nom">Nom&nbsp;: </label><input type="text" id="nom" name="nom" value="${r['nom']}" size="50"></p>$mdp
+    <p class="ligne"><label for="mdp1_$id">Mot de passe&nbsp;: </label><input type="password" id="mdp1_$id" name="mdp1" value=""></p>
+    <p class="ligne"><label for="mdp2_$id">Confirmation&nbsp;: </label><input type="password" id="mdp2_$id" name="mdp2" value=""></p>
+    <input type="hidden" name="id" value="$id">
+  </form>
+  </div>
+
 
 FIN;
 }
-?>
-      </tbody>
-    </table>
-  </div>
+$resultat->free();
+$mysqli->close();
 
-<?php
 // Bas de page
 include('bas.php');
 ?>
