# Prérequis serveur — local_kid

Note technique à l'usage de tout administrateur amené à déployer ou héberger
`local_kid` (KID — Évaluation par compétences) sur une instance Moodle.
Établie par inspection directe du code du plugin (version `0.3.1`,
`2026071601`), pas par supposition.

## Moodle

- **Version minimale** : Moodle **4.5** (`$plugin->requires = 2024100100`,
  branche `405`). Le plugin n'a pas été testé sur une version antérieure ou
  une branche majeure ultérieure — vérifier la compatibilité avant toute
  migration vers Moodle 5.x.
- **Base de données** : le schéma (`db/install.xml`, 21 tables) utilise
  exclusivement l'API XMLDB standard de Moodle — aucune requête SQL brute
  ni fonctionnalité spécifique à un moteur particulier. Compatible avec
  tout moteur supporté par Moodle 4.5 (PostgreSQL, MySQL/MariaDB). Testé en
  production sur **PostgreSQL 17**.
- **Migrations** : `db/upgrade.php` présent — les mises à jour de version
  suivent le mécanisme standard `admin/cli/upgrade.php`. ⚠ Sur un serveur
  dont le PHP servant les requêtes web est en version ≥ 8.4, Moodle 4.5
  bloque `admin/cli/upgrade.php` par une restriction d'environnement
  (`restrict_php_version_84`) — prévoir soit PHP 8.3, soit un contournement
  CLI ciblé (voir note serveur interne, non spécifique à ce plugin).

## PHP

- **Extensions requises** (héritées de PHPWord vendorisé, voir plus bas) :
  `ext-dom`, `ext-gd`, `ext-zip`, `ext-json`, `ext-xml`. Ce sont en plus des
  extensions déjà exigées par Moodle 4.5 lui-même (`intl`, `mbstring`,
  `curl`, `xmlreader`, `xmlwriter`, `soap`, etc. — voir la documentation
  Moodle standard, non spécifiques à `local_kid`).
- **`ZipArchive`** est utilisé directement par le plugin (pas seulement via
  PHPWord) dans `pages/bulk_fiche_zip.php` pour l'export groupé des fiches
  élèves — nécessite `ext-zip` chargé et le compte du serveur web
  (`www-data`) autorisé à écrire un fichier ZIP temporaire.
- **`memory_limit` / `max_execution_time`** : le plugin ne modifie **pas**
  ces réglages lui-même (aucun `ini_set()`/`set_time_limit()` dans le code).
  Les exports lourds — génération DOCX par étudiant puis mise en ZIP pour
  une promotion entière (`bulk_fiche_zip.php`) — dépendent donc entièrement
  des valeurs `php.ini` du pool PHP-FPM servant Moodle. **Recommandation** :
  pour une promotion de taille normale (quelques dizaines d'étudiants),
  prévoir au minimum `memory_limit >= 256M` et `max_execution_time >= 120`
  sur les requêtes du plugin — les valeurs par défaut restrictives de
  Moodle (souvent 128M/30s) peuvent ne pas suffire et provoquer un export
  tronqué ou une erreur `Fatal error: Allowed memory size exhausted`
  silencieuse côté utilisateur.

## Bibliothèques tierces vendorisées

`local_kid` embarque directement (dans `vendor/`, **pas** via `composer
install` au déploiement — le dossier `vendor/` versionné suffit) :

| Bibliothèque | Version | Usage |
|---|---|---|
| `phpoffice/phpword` | 1.4.0 | Génération des fiches élèves au format DOCX (phase 7) |
| `phpoffice/math` | 0.3.0 | Dépendance de PHPWord (conversion OMML/LaTeX) |

Aucune installation Composer n'est nécessaire sur le serveur cible : le
dossier `vendor/` fait partie intégrante du plugin et doit être copié tel
quel avec le reste du code (déjà le cas dans le dépôt Git et dans toute
sauvegarde du code complet).

### Dépendance adoptée — tc-lib-pdf, pour PDF/UA

✅ **Statut au 2026-08-17 : intégrée.** `pdf_service.php` et
`capp_rapport_pdf.php` génèrent désormais les deux exports PDF du plugin
(livret CAPP, fiche élève) via `tecnickcom/tc-lib-pdf` — TCPDF/Moodle
`\pdf` n'est plus utilisé par local_kid du tout. Livret CAPP vérifié
**100% conforme PDF/UA-1** (veraPDF 1.30.2, profil `ua1`, 0 échec) sur des
documents réels (promotion 34, 88 élèves, session CAPP 3) et via
téléchargement HTTP réel, pas seulement en CLI.

Contexte : TCPDF (déjà vendorisé par Moodle lui-même, voir plus bas) ne
sait pas produire de PDF taggé (`StructTreeRoot`/`MarkInfo` présents dans
son code mais commentés, jamais émis — vérifié par lecture directe de
`lib/tcpdf/tcpdf.php`). `tecnickcom/tc-lib-pdf`, successeur officiel de
TCPDF par le même auteur, a un vrai support PDF/UA-1/UA-2 vérifié par
lecture de son code source (`StructTreeRoot` réellement écrit, rôles
`TH`/`TD` différenciés, `Figure + /Alt` pour les images, marquage
`/Artifact` pour les en-têtes/pieds de page). Fonctionnalité récente
(introduite en avril 2026), encore en stabilisation.

**Patch local vendorisé, à connaître avant toute mise à jour de
`tc-lib-pdf`** : `vendor/tecnickcom/tc-lib-pdf/src/Output.php`,
méthode `setPageStructParents()`, porte un correctif local_kid (commentaire
`LOCAL PATCH` dans le code) — la version amont utilise `str_replace()` sans
ancrage sur un motif comme `"N 0 obj\n<<\n/Type /Page\n"`, qui matche aussi
à l'intérieur du numéro d'objet d'une AUTRE page se terminant par les mêmes
chiffres (ex. l'aiguille de la page objet `5` matche aussi dans l'objet
`15`), corrompant sa clé `/StructParents` (doublon détecté par veraPDF dès
qu'un livret dépasse ~10 pages — le cas de tout CAPP réel). Confirmé
indépendant de tout code local_kid par reproduction minimale hors plugin.
**Un `composer update` sur `tecnickcom/tc-lib-pdf` écrasera ce patch** —
réappliquer le correctif (ou vérifier qu'il a été corrigé en amont) avant
tout redéploiement après une mise à jour. À signaler sur
https://github.com/tecnickcom/tc-lib-pdf.

**Police Marianne pré-convertie** : `vendor/tecnickcom/tc-lib-pdf-font/target/fonts/marianne/`
contient les 4 variantes (regular/bold/italique/gras-italique) au format
interne de tc-lib-pdf, générées une fois depuis `fonts/*.ttf` via la classe
`Com\Tecnick\Pdf\Font\Import`. Pour régénérer si les TTF sources changent :
voir le script utilisé dans l'historique Git (`Import` appelé une fois par
fichier, sortie dans ce même dossier).

⚠ **Écart corrigé le 2026-08-27** : cette section affirmait depuis le départ
que `target/fonts/` (Marianne + toutes les polices de secours/internes de
tc-lib-pdf-font — dejavu, core, cid0, freefont, unifont, pdfa) était
« committé dans le dépôt ». C'était faux : le `.gitignore` propre au paquet
tiers `tecnickcom/tc-lib-pdf-font` (`target`, `target/fonts`, non désactivé
par local_kid) excluait silencieusement tout ce dossier du dépôt Git — un
`git clone` frais en était totalement dépourvu, alors que ça fonctionnait
en production uniquement parce que ces fichiers existaient déjà sur le
disque du serveur (générés une fois le 2026-08-17, jamais supprimés).
Corrigé le 2026-08-27 : tout `target/fonts/` (42 Mo, 187 fichiers) a été
ajouté au dépôt via `git add -f`, pour être cohérent avec le reste de la
politique « `vendor/` versionné tel quel, aucune étape de build au
déploiement » de ce document. Vérifié par clonage frais avant/après.

**Si cette dépendance est adoptée, elle change la nature du prérequis
serveur par rapport à PHPWord ci-dessus** — point important pour tout
transfert vers un autre serveur :

- **PHP ≥ 8.2** requis par `tc-lib-pdf` (`composer.json`) — déjà satisfait
  par ce serveur (PHP 8.4.5) et par toute instance Moodle 4.5+, donc sans
  impact en pratique, mais à vérifier explicitement si le plugin est un
  jour porté sur un serveur avec un PHP plus ancien que 4.5 ne l'exigerait
  pas nativement.
- **Extensions PHP supplémentaires** exigées par `tc-lib-pdf` :
  `ext-ctype`, `ext-filter`, `ext-hash`, `ext-json`, `ext-mbstring`,
  `ext-openssl`, `ext-pcre`, `ext-xml`, `ext-zlib`. Vraisemblablement déjà
  présentes sur toute instance Moodle standard, mais **non encore vérifiées
  une par une** sur ce serveur pour ce paquet précis tant que le spike n'a
  pas été fait.
- **Contrairement à PHPWord, le vendoring à la main n'est plus réaliste** :
  `tc-lib-pdf` entraîne **13 autres paquets `tecnickcom/tc-lib-*`**
  (barcode, color, pdf-image, pdf-font, file, pdf-encrypt, pdf-sign,
  unicode-data, unicode, pdf-page, pdf-graph, pdf-parser), tous liés par
  des contraintes de version Composer. Deux options à trancher si retenu,
  avec un impact direct sur ce que "transférer le plugin" veut dire :
  1. **`vendor/` entièrement résolu committé** dans le dépôt (même logique
     que PHPWord aujourd'hui) — le serveur cible n'a toujours besoin de
     rien installer, mais le dépôt grossit nettement et toute mise à jour
     de version doit être refaite avec Composer sur un poste de dev puis
     re-committée.
  2. **`composer install --no-dev` comme étape de déploiement réelle** sur
     le serveur cible — dépôt plus léger, mais **Composer devient un
     prérequis serveur à part entière** (absent de ce VPS au 2026-08-17),
     et un accès réseau sortant vers Packagist/GitHub est nécessaire *au
     moment du déploiement/de la mise à jour* — première exception à la
     règle "aucun appel réseau sortant" de la section Réseau ci-dessous, à
     documenter clairement si cette option est choisie (l'exception reste
     limitée au déploiement, jamais une requête utilisateur en runtime).
- **veraPDF (+ JRE) n'est PAS un prérequis serveur** : c'est un outil de
  **vérification en développement uniquement**, jamais appelé par le
  plugin ni par Moodle en runtime. Ne pas l'installer sur un serveur de
  production — seulement sur le poste/serveur utilisé pour auditer la
  conformité des PDF générés avant/après une évolution du générateur.

**Checklist si transfert de KID vers un nouveau serveur, une fois
`tc-lib-pdf` effectivement intégré** : PHP ≥ 8.2 + extensions listées
ci-dessus → soit copier le `vendor/` complet tel quel (option 1), soit
installer Composer sur ce serveur et lancer `composer install --no-dev`
depuis la racine du plugin (option 2) → dans les deux cas, revérifier la
propriété/droits `www-data` du `vendor/` résultant (piège déjà rencontré
sur ce projet avec les polices et logos, voir plus bas).

## Tâche planifiée — ⚠ point bloquant si le cron Moodle ne tourne pas

`local_kid` déclare une tâche planifiée native Moodle :

```
local_kid\task\sync_moodle_feedback_task — toutes les 30 minutes (*/30)
```

(`db/tasks.php`). Cette tâche **ne s'exécute que si `admin/cli/cron.php`
tourne réellement** sur le serveur (via une entrée crontab standard) — ce
n'est **pas** automatique du simple fait que Moodle est installé.

⚠ **Constat fait sur le serveur de production actuel (brunobernard.cloud)
le 2026-08-03** : aucune tâche cron Moodle n'y est configurée (ni crontab
`ubuntu`, `www-data`, `root`, ni `/etc/cron.d/`, ni timer systemd) — donc
`sync_moodle_feedback_task` **ne s'exécute pas** sur cette instance
actuellement, malgré le plugin fonctionnel par ailleurs. Sur toute nouvelle
instance de déploiement, ajouter impérativement une entrée crontab, par
exemple :
```
* * * * * sudo -u www-data php /var/www/docs/admin/cli/cron.php >> /var/log/moodle-cron.log 2>&1
```
et vérifier après coup que la tâche `local_kid\task\sync_moodle_feedback_task`
apparaît bien exécutée dans *Administration du site > Serveur > Tâches >
Tâches planifiées*.

## Réseau

Aucun appel réseau sortant propre au plugin (pas de client HTTP, pas
d'appel API externe — vérifié par recherche exhaustive de `curl_init`,
`file_get_contents` sur une URL, ou client Guzzle dans le code hors
`vendor/`). Le seul export lié à un système externe (ADESI) génère un
fichier téléchargé par l'utilisateur ; il n'y a pas d'appel sortant direct
vers un service ADESI. Les seules dépendances réseau viennent de Moodle
lui-même (LDAP, SMTP), pas de `local_kid`.

## Permissions fichiers

⚠ **Écart constaté le 2026-08-17** entre cette section (qui documentait la
convention Moodle standard) et l'état réel du serveur de production actuel.
La convention standard Moodle — vérifiée sur `mod/`, `local/evalinterface`
et `local/kid/version.php` lui-même — est bien `root:www-data`, mode `640`
fichiers / `750` répertoires (root possède le code, `www-data` le lit
seul). **Mais le contenu réel de `local/kid/` a dérivé vers
`www-data:www-data`, mode `664`/`775`**, au fil des correctifs appliqués
directement sur ce serveur (polices, logos, code PHP) sans repasser par le
mécanisme d'upgrade standard. Les deux conventions fonctionnent (PHP-FPM
tourne en `www-data`, donc lit les deux), mais **ne pas mélanger les deux
à l'intérieur du même plugin** sans le vouloir : toute future copie
manuelle de fichier dans `local/kid/` sur *ce* serveur doit reproduire
`www-data:www-data` (l'état réel actuel), pas `root:www-data` (l'état
"propre" théorique) — un mélange des deux ne casse rien en soi, mais rend
incohérente une future comparaison `diff`/`rsync` entre dev et prod. Sur
un **nouveau** serveur (transfert), repartir de la convention standard
`root:www-data` est recommandé plutôt que de reproduire cette dérive.

Dans tous les cas : un oubli de propriétaire/mode rend les nouvelles
classes invisibles à l'autoloader PHP-FPM, sans erreur visible en CLI.

## Espace disque

Empreinte du code ~72 Mo (mesuré le 2026-08-27 hors `.git`, incluant
`vendor/` et le dossier `target/fonts/` de tc-lib-pdf-font — 42 Mo à lui
seul, voir la section Bibliothèques tierces ci-dessus). Négligeable au
regard des ressources serveur mais nettement plus que les 5,7 Mo mesurés le
2026-08-17 avant que ce dossier de polices soit effectivement committé. Le
volume réel dépend en plus des données
stockées via l'API fichiers Moodle (`moodledata`) pour les documents
importés/exportés (fiches élèves, pièces justificatives de stage) — pas
de dimensionnement disque spécifique documenté à ce jour, à surveiller au
fur et à mesure de l'usage réel.

## Export / import vers un autre serveur Moodle

⚠ **Ne pas utiliser l'upload ZIP via l'interface** (*Administration du
site > Greffons > Installer des greffons*, `admin/tool/installaddon`) pour
ce plugin. Vérifié sur ce serveur le 2026-08-17 : le pool PHP-FPM qui sert
les requêtes web limite `upload_max_filesize=2M`/`post_max_size=8M`
(`/etc/php/8.4/fpm/php.ini`), alors que le plugin pèse déjà 5,7 Mo hors
`.git` — l'upload web échouerait probablement dès aujourd'hui, sans même
compter une éventuelle dépendance `tc-lib-pdf` future (voir plus haut). La
méthode fiable, **sans limite de taille**, est la copie fichier directe
(déjà celle utilisée en pratique pour ce projet) suivie du mécanisme
d'upgrade standard Moodle.

### 1. Export — sur le serveur source

```bash
# Archiver le plugin en excluant l'historique Git (inutile en prod) ;
# tar préserve les permissions, un seul fichier à transférer plutôt que
# les ~2100 fichiers un par un.
cd /var/www/docs/local
sudo tar --exclude='kid/.git' -czf /tmp/kid-$(date +%Y%m%d).tar.gz kid/

# Vérification avant transfert
tar tzf /tmp/kid-$(date +%Y%m%d).tar.gz | wc -l
du -sh /tmp/kid-$(date +%Y%m%d).tar.gz
sudo chown "$USER" /tmp/kid-$(date +%Y%m%d).tar.gz   # pour pouvoir le scp sans sudo
```

### 2. Transfert — scp

```bash
# Depuis un poste ayant accès SSH aux deux serveurs (le plus simple : un
# transfert direct serveur-à-serveur si le serveur source peut atteindre
# la cible en SSH) :
scp /tmp/kid-$(date +%Y%m%d).tar.gz <utilisateur>@<NOUVEAU_SERVEUR>:/tmp/

# Ou en deux temps, via un poste intermédiaire qui n'a accès qu'à l'un des
# deux serveurs à la fois :
scp <utilisateur>@<SERVEUR_SOURCE>:/tmp/kid-$(date +%Y%m%d).tar.gz ./
scp ./kid-$(date +%Y%m%d).tar.gz <utilisateur>@<NOUVEAU_SERVEUR>:/tmp/
```

### 3. Installation — sur le nouveau serveur

```bash
# Adapter le chemin réel du dirroot Moodle cible (ici celui de cette infra,
# /var/www/docs, à vérifier sur le nouveau serveur — peut être
# /var/www/moodle, /var/www/html, etc.)
DIRROOT=/var/www/docs

cd "$DIRROOT/local"
sudo tar xzf /tmp/kid-$(date +%Y%m%d).tar.gz

# Convention Moodle standard recommandée sur un nouveau serveur (voir
# écart documenté dans la section Permissions fichiers ci-dessus) :
sudo chown -R root:www-data "$DIRROOT/local/kid"
sudo find "$DIRROOT/local/kid" -type d -exec chmod 750 {} \;
sudo find "$DIRROOT/local/kid" -type f -exec chmod 640 {} \;

# Déclenche l'installation : xmldb_local_kid_install() crée les rôles
# fonctionnels (role_setup::create_functional_roles()) et sème les 5
# niveaux fixes automatiquement — aucune étape manuelle supplémentaire.
sudo -u www-data php "$DIRROOT/admin/cli/upgrade.php" --non-interactive

# ⚠ Si le PHP servant les requêtes web du nouveau serveur est en version
# ≥ 8.4, cette commande peut être bloquée par la restriction Moodle 4.5
# restrict_php_version_84 (voir section Moodle en haut de ce document) —
# prévoir le contournement CLI correspondant dans ce cas, ou passer par
# l'upgrade web (Administration > Notifications) si le PHP web diffère du
# PHP CLI utilisé ici.
```

### 4. Vérifications après installation

```bash
# Le plugin apparaît bien et à la bonne version
sudo -u www-data php -r '
define("CLI_SCRIPT", true);
require("'"$DIRROOT"'/config.php");
$plugin = core_plugin_manager::instance()->get_plugin_info("local_kid");
echo $plugin->displayname." — version ".$plugin->versiondb."\n";
'

# Les 4 rôles fonctionnels ont bien été créés (gest_scolarite, gest_stages,
# membre_capp, + le rôle complet — voir role_setup.php)
sudo -u www-data php -r '
define("CLI_SCRIPT", true);
require("'"$DIRROOT"'/config.php");
global $DB;
print_r($DB->get_records_menu("role", null, "", "shortname, id"));
'
```

Puis, impérativement, ajouter l'entrée crontab pour `admin/cli/cron.php`
(voir section "Tâche planifiée" ci-dessus) — sans elle,
`sync_moodle_feedback_task` n'est jamais exécutée sur la nouvelle
instance, comme actuellement constaté sur ce serveur.
