Partie B — Programmation web (8 points), dans l’esprit des sessions 2024–2025
Le site de l’atelier VéloCité est composé des pages reparation.html, atelier.css, atelier.js et reparation.php. La page reparation.html permet à un technicien d’enregistrer une réparation dans la base de données « velocite ». La figure 1 montre la page juste après son chargement.
Figure 2 — code HTML de reparation.html (les lignes en pointillés sont à compléter) :
1 <!DOCTYPE html>
2 <html lang="fr">
3 <head>
4 <meta charset="utf-8">
5 <title>VéloCité - Atelier</title>
6 ………………………………………
7 <script src="atelier.js"></script>
8 </head>
9 <body onload="inconnue()">
10 <header>
11 <h1>VéloCité : atelier de réparation</h1>
12 <nav>
13 <a href="index.html">Accueil</a>
14 <a href="stock.html">Stock des pièces</a>
15 </nav>
16 </header>
17 <form id="f" name="f" action="reparation.php" method="post" onsubmit="return verifier()">
18 <fieldset>
19 ………………………………………
20 <label>N° du vélo :</label>
21 <input type="number" id="velo" name="idVelo" min="101"><br>
22 <label>Date :</label>
23 <input type="date" id="date" name="dateRep"><br>
24 <label>Panne :</label>
25 <select id="panne" name="panne">
26 <option value="">Choisir une panne</option>
27 <option>Chaîne cassée</option>
28 <option>Pneu crevé</option>
29 <option>Frein avant</option>
30 <option>Batterie</option>
31 </select><br>
32 <label>Pièces changées :</label>
33 <input type="checkbox" name="piece[]" value="12.500" onclick="calculer()">Chaîne
34 <input type="checkbox" name="piece[]" value="9.000" onclick="calculer()">Pneu
35 <input type="checkbox" name="piece[]" value="15.000" onclick="calculer()">Plaquettes
36 <input type="checkbox" name="piece[]" value="95.000" onclick="calculer()">Batterie<br>
37 <label>Main-d'œuvre :</label>
38 <input type="range" id="mo" name="mo" min="0" max="30" step="5" onchange="calculer()">
39 <span id="valMo"></span> D<br>
40 <label>Coût total :</label>
41 <input type="text" id="cout" name="cout" readonly> D<br>
42 <textarea id="remarque" name="remarque" rows="3" cols="30" placeholder="Remarque du technicien"></textarea><br>
43 <input type="submit" value="Enregistrer">
44 ………………………………………
45 </fieldset>
46 </form>
47 ………………………………………
48 </body>
49 </html>Figure 3 — code de atelier.js :
1 function calculer()
2 {
3 const pieces = document.getElementsByName("piece[]")
4 let s = 0
5 for (let i = 0; i < pieces.length; i++)
6 if (pieces[i].checked)
7 s = s + parseFloat(pieces[i].value)
8 let mo = document.getElementById("mo").value
9 document.getElementById("valMo").innerHTML = mo
10 s = s + Number(mo)
11 document.getElementById("cout").value = s.toFixed(3)
12 }
13 function inconnue()
14 {
15 const pieces = document.getElementsByName("piece[]")
16 for (let i = 0; i < pieces.length; i++)
17 pieces[i].checked = false
18 document.getElementById("mo").value = 10
19 calculer()
20 }
21 function verifier()
22 {
23 const v = document.getElementById("velo").value
24 if (v == "" || isNaN(v))
25 { alert("Numéro de vélo invalide")
26 return false }
27 if (document.getElementById("panne").selectedIndex == 0)
28 { alert("Choisir une panne")
29 return false }
30 if (Number(document.getElementById("cout").value) == 0)
31 { alert("Le coût ne peut pas être nul")
32 return false }
33 return true
34 }Figure 4 — code de reparation.php :
1 <?php
2 $con = mysqli_connect("localhost", "root", "", "velocite") or die("Erreur B.D.");
3 $velo = $_POST["idVelo"];
4 $date = $_POST["dateRep"];
5 $panne = $_POST["panne"];
6 $cout = $_POST["cout"];
7 $res1 = mysqli_query($con, "select codeModele from Velo where idVelo = $velo")
8 or die("Requête erronée");
9 if ($lig = mysqli_fetch_row($res1))
10 {
11 echo "Vélo $velo (modèle $lig[0])<br>";
12 $req = "insert into Reparation values ($velo, '$date', '$panne', $cout)";
13 $res2 = mysqli_query($con, $req);
14 if (mysqli_affected_rows($con) == 1)
15 echo "Réparation enregistrée : $panne, $cout D";
16 else
17 echo "Échec : " . mysqli_error($con);
18 }
19 else
20 echo "Le vélo $velo n'existe pas.";
21 mysqli_close($con);
22 ?>Figure 5 — extraits des tables Velo et Reparation ; la clé primaire de Reparation est le couple (idVelo, dateRep).
Velo
| idVelo | dateService | codeModele | idStation |
|---|---|---|---|
| 101 | 2017-05-10 | M1 | 1 |
| 102 | 2018-03-02 | M2 | 2 |
| 103 | 2021-06-15 | M5 | 3 |
| 104 | 2023-09-01 | M6 | 3 |
| 105 | 2024-02-20 | M5 | 2 |
| 106 | 2019-11-11 | M1 | 4 |
Reparation
| idVelo | dateRep | panne | cout |
|---|---|---|---|
| 101 | 2025-03-01 | Chaîne cassée | 18.000 |
| 103 | 2025-08-12 | Batterie | 120.000 |
| 104 | 2025-10-05 | Frein avant | 25.000 |
| 101 | 2026-01-15 | Pneu crevé | 12.000 |
| 105 | 2026-02-02 | Écran de bord | 85.000 |
N.B. : les fonctions mysqli_… admettent leurs équivalents mysql_… ; comme dans les sujets, une requête qui échoue fait retourner false à mysqli_query.
Exercice 1 — analyse et interprétation du code HTML (3 points)
1. Indiquer devant chaque rôle le numéro de la ligne contenant le code HTML qui lui correspond.
| Rôle | N° de ligne |
|---|---|
| Spécifier l’encodage des caractères de la page web. | |
| Lier un fichier de scripts externe à la page web. | |
| Regrouper les liens de navigation de la page. | |
| Créer une zone de saisie de texte sur plusieurs lignes. | |
| Afficher une valeur que l’utilisateur ne peut pas modifier, mais qui est envoyée avec le formulaire. | |
| Choisir une valeur numérique dans une plage à l’aide d’un curseur. |
2. Pour chacune des affirmations suivantes, mettre une croix dans la case correspondant à la proposition correcte.
- L’attribut
step="5"(ligne 38) signifie que : ☐ la valeur du curseur varie de 5 en 5 ☐ le curseur ne propose que 5 positions ☐ la valeur initiale du curseur est 5 - L’attribut
placeholder(ligne 42) : ☐ définit le texte envoyé par défaut au serveur ☐ affiche un texte indicatif tant que la zone est vide, sans l’envoyer ☐ définit le titre de la zone de saisie - L’option de la ligne 29, qui n’a pas d’attribut value, envoie au serveur lorsqu’elle est choisie : ☐ une chaîne vide ☐ le texte « Frein avant » ☐ son rang, 3
- L’attribut
readonly(ligne 41) : ☐ empêche l’utilisateur de modifier le champ, dont la valeur est envoyée ☐ masque le champ dans la page ☐ empêche l’envoi de la valeur du champ
3. Pour compléter le code HTML, écrire devant chaque balise le numéro de la ligne dans laquelle elle doit être insérée.
| Balise | N° de ligne |
|---|---|
<input type="reset" value="Annuler"> | |
<footer>VéloCité - atelier de Sfax</footer> | |
<legend>Nouvelle réparation</legend> | |
<link rel="stylesheet" href="atelier.css"> |
Exercice 2 — analyse et interprétation du code CSS (1,5 point)
En se basant sur les figures 1 et 2, écrire le contenu du fichier atelier.css correspondant à chaque mise en forme demandée, en utilisant les instructions fournies dans la liste suivante : legend | #cout | .cout | nav a | a nav | color : white | background-color : #1d6b8f | padding : 4px | margin : 4px | font-weight : bold | font-style : bold | text-align : right | width : 90px | text-decoration : none | text-decoration : underline | margin-right : 15px | border : none.
| Mise en forme | Code CSS |
|---|---|
| Le titre « Nouvelle réparation » du cadre doit avoir : un texte blanc ; un arrière-plan de couleur #1d6b8f ; une marge intérieure de 4 pixels. | ………… { ………… } |
| Le champ « Coût total » doit avoir : un texte en gras ; un texte aligné à droite ; une largeur de 90 pixels. | ………… { ………… } |
| Les liens du menu de navigation doivent avoir : un texte non souligné ; une marge extérieure droite de 15 pixels. | ………… { ………… } |
Justifier le choix du sélecteur de la deuxième règle.
Exercice 3 — analyse et interprétation du code JavaScript (2 points)
1. Indiquer devant chaque rôle l’élément du langage JavaScript (figure 3) qui lui correspond.
| Rôle | Élément |
|---|---|
| Fonction qui convertit une chaîne en nombre réel. | |
| Méthode qui écrit un nombre avec un nombre fixé de chiffres après la virgule. | |
| Fonction qui teste si une valeur n’est pas un nombre. | |
| Méthode qui retourne la liste des éléments ayant le même nom. | |
| Propriété qui indique si une case est cochée ou non. | |
| Propriété qui donne le rang de l’option choisie dans une liste déroulante. |
2. Le technicien coche « Chaîne » puis « Plaquettes » et place le curseur sur 15. Compléter la trace de la boucle de calculer() lors de son dernier appel (une ligne par tour), puis donner le contenu de valMo et la valeur du champ cout.
3. Si la ligne 7 était s = s + pieces[i].value, que contiendrait s à la sortie de la boucle, pour le même exemple ? Expliquer.
4. a. À quel moment la fonction inconnue() est-elle exécutée ?
b. En s’aidant de la figure 1, en déduire son rôle.
5. Le technicien saisit le vélo 104, choisit « Frein avant », ne coche aucune pièce, place le curseur sur 0 et clique sur « Enregistrer ». Quelle valeur retourne verifier() et quel message est affiché ? Le formulaire est-il envoyé ?
Exercice 4 — analyse et interprétation du code PHP (1,5 point)
Le technicien saisit le vélo 104, la date 2026-03-20, choisit « Frein avant », coche « Plaquettes », laisse la main-d’œuvre à 10 et clique sur « Enregistrer ».
1. Compléter le tableau suivant.
| Variable (figure 4) | Ligne du champ (figure 2) | Valeur reçue |
|---|---|---|
$velo (ligne 3) | | | ||
$date (ligne 4) | | | ||
$panne (ligne 5) | | | ||
$cout (ligne 6) | | |
2. Pour chacune des affirmations suivantes, mettre une croix dans la case correspondant à la proposition correcte.
- L’instruction de la ligne 9 peut être remplacée par : ☐
if ($lig = mysqli_fetch_array($res1))☐if ($lig = mysqli_affected_rows($res1))☐if ($lig = mysqli_num_rows($res1)) - L’instruction de la ligne 14 peut être remplacée par (
$res2étant le résultat de la ligne 13) : ☐if (mysqli_num_rows($res2) == 1)☐if ($res2)☐if (mysqli_fetch_row($res2)) - Pour récupérer la main-d’œuvre, un élève ajoute au script l’instruction
$mo = $_POST["valMo"];. Cette instruction : ☐ récupère la valeur de la main-d’œuvre ☐ ne récupère rien : un span n’est pas envoyé, il fallait lire la clé "mo" ☐ récupère le texte « 10 D »
3. Donner l’affichage produit par reparation.php pour cet exemple. Que se passe-t-il si le technicien envoie une seconde fois le même formulaire ?
Voir la correction commentéeAprès avoir posé votre démarche
Exercice 1.1 Idée : nommer d’abord la balise ou l’attribut qui réalise le rôle, puis chercher sa ligne.
| Rôle | Code | Ligne |
|---|---|---|
| Spécifier l’encodage des caractères de la page web. | <meta charset="utf-8"> | 4 |
| Lier un fichier de scripts externe à la page web. | <script src="atelier.js"> | 7 |
| Regrouper les liens de navigation de la page. | <nav> | 12 |
| Créer une zone de saisie de texte sur plusieurs lignes. | <textarea> | 42 |
| Afficher une valeur que l’utilisateur ne peut pas modifier, mais qui est envoyée avec le formulaire. | readonly | 41 |
| Choisir une valeur numérique dans une plage à l’aide d’un curseur. | <input type="range"> | 38 |
Exercice 1.2 Idée : chaque attribut a un effet précis sur l’affichage ou sur l’envoi.
- 1. La valeur du curseur varie de 5 en 5 : de 0 à 30, elle prend les 7 valeurs 0, 5, 10, 15, 20, 25, 30.
- 2. Un texte indicatif affiché tant que la zone est vide, sans être envoyé (sans saisie, remarque est envoyée vide).
- 3. Le texte « Frein avant » : sans attribut value, une option a pour valeur son texte (ce n’est pas son rang, 3).
- 4. L’utilisateur ne peut pas modifier le champ, dont la valeur est envoyée : readonly bloque la saisie, pas l’envoi (disabled, lui, empêcherait l’envoi).
Exercice 1.3 Idée : link va dans head ; legend suit immédiatement fieldset ; le bouton reset accompagne le bouton submit ; footer ferme le body.
| Balise | Ligne |
|---|---|
<input type="reset" value="Annuler"> | 44 |
<footer>VéloCité - atelier de Sfax</footer> | 47 |
<legend>Nouvelle réparation</legend> | 19 |
<link rel="stylesheet" href="atelier.css"> | 6 |
Exercice 2 Idée : le sélecteur se lit sur l’attribut réellement présent dans le HTML, puis chaque demande correspond à un couple propriété : valeur de la liste.
legend { color : white ; background-color : #1d6b8f ; padding : 4px ; }
#cout { font-weight : bold ; text-align : right ; width : 90px ; }
nav a { text-decoration : none ; margin-right : 15px ; }Le champ Coût total porte id="cout" (ligne 41) et aucune classe : on le vise par le sélecteur d’identifiant #cout, pas .cout. Le titre du cadre est la balise <legend> ; nav a (espace = descendant) vise les liens situés dans nav, alors que a nav viserait un nav contenu dans un lien. Marge intérieure = padding ; gras = font-weight (font-style règle l’italique) ; non souligné = text-decoration : none.
Exercice 3.1
| Rôle | Élément |
|---|---|
| Fonction qui convertit une chaîne en nombre réel. | parseFloat() |
| Méthode qui écrit un nombre avec un nombre fixé de chiffres après la virgule. | toFixed() |
| Fonction qui teste si une valeur n’est pas un nombre. | isNaN() |
| Méthode qui retourne la liste des éléments ayant le même nom. | getElementsByName() |
| Propriété qui indique si une case est cochée ou non. | checked |
| Propriété qui donne le rang de l’option choisie dans une liste déroulante. | selectedIndex |
Exercice 3.2 Idée : dérouler la boucle case par case ; seules les cases cochées ajoutent leur value, convertie par parseFloat.
| i | Pièce | pieces[i].checked | s en fin de tour |
|---|---|---|---|
| 0 | Chaîne | true | 12.5 |
| 1 | Pneu | false | 12.5 |
| 2 | Plaquettes | true | 27.5 |
| 3 | Batterie | false | 27.5 |
Puis mo = "15" : valMo affiche 15 ; s = 27.5 + 15 = 42.5, et le champ cout reçoit s.toFixed(3) = "42.500".
Exercice 3.3 value est une chaîne : sans parseFloat, + concatène. s passe de 0 à 0 + "12.500" = "012.500", puis à "012.500" + "15.000" = "012.50015.000" : une chaîne, et non le nombre 27.5.
Exercice 3.4 a. Au chargement de la page, grâce à onload="inconnue()" (ligne 9).
b. inconnue() initialise la zone de calcul : elle décoche les quatre pièces, place le curseur de main-d’œuvre sur 10, puis appelle calculer(), qui affiche 10 à côté du curseur et 10.000 dans le coût total — l’état visible sur la figure 1.
Exercice 3.5 Le numéro 104 est valide et l’option choisie a le rang 3 (≠ 0). Aucune pièce et une main-d’œuvre nulle donnent un coût "0.000" : Number("0.000") vaut 0 (ligne 30). verifier() affiche « Le coût ne peut pas être nul » et retourne false : grâce à return verifier() (ligne 17 de reparation.html), le formulaire n’est pas envoyé.
Exercice 4.1 Idée : chaque clé est le name d’un champ ; la valeur est celle du champ au moment de l’envoi.
| Variable | Ligne du champ | Valeur reçue |
|---|---|---|
$velo | 21 (name="idVelo") | "104" | ||
$date | 23 (name="dateRep") | "2026-03-20" | ||
$panne | 25 (name="panne") | "Frein avant" (texte de l’option, qui n’a pas de value) | ||
$cout | 41 (name="cout") | "25.000" (15.000 + 10, calculé par calculer() ; readonly n’empêche pas l’envoi) |
Exercice 4.2 Idée : se demander ce que renvoie chaque fonction et sur quoi elle agit.
- 1.
if ($lig = mysqli_fetch_array($res1)): fetch_array donne aussi l’indice 0, donc$lig[0](ligne 11) fonctionne toujours. affected_rows prend la connexion, pas un résultat ; avec num_rows,$ligvaudrait un nombre et le modèle ne serait pas affiché. - 2.
if ($res2): l’insertion d’une seule ligne réussit si et seulement si mysqli_query retourne true, c’est-à-dire si$res2est vrai. num_rows et fetch_row ne s’appliquent qu’au résultat d’un SELECT. - 3. Elle ne récupère rien : le span valMo (ligne 39) n’est pas un champ (clé indéfinie) ; la main-d’œuvre est envoyée par le curseur, sous la clé "mo".
Exercice 4.3 Le vélo 104 existe (modèle M6) et la réparation (104, 2026-03-20) n’est pas encore enregistrée : l’insertion touche une ligne. Affichage :
Vélo 104 (modèle M6)
Réparation enregistrée : Frein avant, 25.000 D
Au second envoi, la première ligne s’affiche de nouveau, mais l’insertion viole la clé primaire (idVelo, dateRep) : le couple (104, 2026-03-20) existe déjà. mysqli_query retourne false, mysqli_affected_rows vaut −1 et le script affiche « Échec : » suivi du message de MySQL signalant une entrée en double pour la clé primaire. La base n’est pas modifiée.