| Semaines | Tâches effectuées |
|---|---|
| 1 | Observation des services dans l'entreprise Exploration du système TODD Propositions d'améliorations (UI et UX) |
| 2 | Mise en place de la station de travail (WSL, Docker, TODD) Débogage |
| 3 | Interface de sélection Création d'un nouveau module Vrai/Faux |
| 4 | Nouvelles interface TODD Création du nouveau module Votes |
| 5 | Nouveau dashboard (design + refactorisation) Conceptualisation et développement de ToddModule (fluent API) Synchronisation des aperçus |
| Semaines | Tâches effectuées |
|---|---|
| 6 | Veille sur Hidden API ESPN Nouveau module Pronostics sportifs basé sur ToddModule Nouveau module Mosaïque basé sur ToddModule |
| 7 | Veille API Open-meteo et geo.api.gouv.fr Nouveau module Météo basé sur ToddModule Dashboard des boitiers Découplage SQL |
| 8 | Conceptualisation et développement de ToddDevice API MyComBox Gestion contenus/boitiers |
| 9 | Débogage synchronisation MyComBox Conception réseau local avec boitiers Raspberry Pi |
Module
autonome
plugins/todd/modules/todd_polls/view.phptodd_polls
├── admin.php
├── dashboard.js
├── form.php
├── handler.php
├── install.php
├── script.js
└── view.php
plugins/todd/modules/todd_polls/view.php<?php
// ...
$votes = $db->getAssocArray("SELECT option_id, count(*) as cnt FROM {DBPFX}todd_polls_votes WHERE poll_id = $poll_id GROUP BY option_id", 'option_id');
$totalVotes = array_sum(array_column($votes, 'cnt'));
?>
<?php ob_start(); ?>
<style>
.poll-container {
max-width: 600px;
margin: 20px auto;
padding: 20px;
background: #fff;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
/* ... */
</style>
<?php $module_head_content = ob_get_clean(); ?>
// ...
<script>
$(document).ready(function() {
// Vote Handler
$('.btn-vote').click(function() {
var btn = $(this);
var pollId = btn.data('poll');
var optionId = btn.data('option');
// ...
</script>
Module
autonome
MVC
todd_polls (1/2)todd_poll
├── admin.php
├── assets
│ ├── css
│ │ ├── admin.css
│ │ └── view.css
│ └── js
│ ├── admin.js
│ └── script.js
├── controllers
│ ├── admin.inc.php
│ ├── payload.inc.php
│ └── view.inc.php
├── handler.php
├── install.php
├── modal_add_voter.php
├── modal_import_csv.php
├── models
│ └── ToddPollModel.php
├── script.js
└── view.php
todd_polls (2/2)<?php
class ToddPollModel {
public static function addVoter($contentId, $firstname, $lastname, $email, $pinCode) {
global $db;
$sql = "INSERT INTO {DBPFX}todd_poll_voters (content_id, firstname, lastname, email, pin_code)
VALUES (:content_id, :firstname, :lastname, :email, :pin_code)";
return $db->dml($sql, [
':content_id' => $contentId,
':firstname' => $firstname,
':lastname' => $lastname,
':email' => $email,
':pin_code' => $pinCode
]);
}
// ...
?>
Module
autonome
MVC
ToddModulePropriétés communes aux modules
abstract class ToddModule
{
protected $instance_id;
protected $module_code;
protected $payload = [];
protected $adminTabs = [];
protected $adminFields = [];
protected $currentTabId = null;
protected $adminViews = [];
...
}
ToddModuleConstructeur
public function __construct($id)
{
$this->instance_id = (int) $id;
$row = ToddContentModel::getContentById($this->instance_id);
if ($row) {
$this->module_code = $row['module_code'];
$dbPayload = !empty($row['payload_json']) ? json_decode($row['payload_json'], true) : [];
$this->payload = array_merge($this->getDefaultPayload(), (is_array($dbPayload) ? $dbPayload : []));
} else {
$this->module_code = strtolower(preg_replace('/(?<!^)[A-Z]/', '_$0', get_class($this)));
$this->payload = $this->getDefaultPayload();
}
$this->setupAdmin();
}
ToddModuleFonctions abstraites
abstract protected function getDefaultPayload(): array;
ToddModuleFluent API
public function addTab(string $id, string $label, string $icon = 'ph-file'): self
{
$this->adminTabs[$id] = [
'id' => $id,
'label' => $label,
'icon' => $icon
];
$this->currentTabId = $id;
return $this;
}
ToddModuleFluent API
public function addField(string $name, string $type, array $options = []): self
{
if (!$this->currentTabId) {
// Default tab if none added
$this->addTab('general', 'General', 'ph-gear');
}
$this->adminFields[$this->currentTabId][] = [
'name' => $name,
'type' => $type,
'options' => $options
];
return $this;
}
todd_betExemple d'utilisation.
├── admin.php
├── assets
│ ├── css
│ │ ├── admin_espn.css
│ │ └── public.css
│ ├── images
│ │ ├── baseball_field.png
│ │ ├── ...
│ └── js
│ ├── admin_espn.js
│ ├── admin.js
│ └── client.js
├── handler.php
├── install.php
├── models
│ ├── ToddBetInstallModel.php
│ └── ToddBetModel.php
├── ToddBet.php
├── view.php
└── views
├── modals
│ └── admin_espn_modal.php
├── public_complete.php
└── public_partial.php
todd_betNouvelle classe ToddBet comme extension de ToddModule
class ToddBet extends ToddModule
{
protected function getDefaultPayload(): array
{
return [
"prono_type" => "1n2",
"team_a_name" => "",
"team_a_logo" => "",
"team_b_name" => "",
"team_b_logo" => "",
...
];
}
...
}
todd_betLa fonction setupAdmin() où on construit l'interface.
protected function setupAdmin(): void
{
$this
->addTab("match", "Match", "ph-soccer-ball")
->addField("prono_type", "select", [
"label" => "Mode de jeu",
"choices" => [
"1n2" => "1N2 (Anonyme)",
"score" => "Score Exact (Avec Pseudo)"
]
])
->addRow([
[
"name" => "team_a_name",
"type" => "text",
"options" => ["label" => "Nom Équipe A"]
],
...
todd_betFichier admin.php
<div class="module-editor hidden" data-module="todd_bet">
<?php
require_once PLUGIN_PATH . 'todd/modules/todd_bet/ToddBet.php';
$toddBetModule = new ToddBet(0);
$reflection = new ReflectionClass($toddBetModule);
$property = $reflection->getProperty('module_code');
$property->setValue($toddBetModule, 'todd_bet');
$toddBetModule->renderAdmin();
?>
</div>
todd_betFichier templates/core_admin_shell.php pour générer le HTML de l'interface de création/édition du module.
if (!isset($module) || !($module instanceof ToddModule)) {
echo '<div class="alert alert-danger">Module configuration error.</div>';
return;
}
$tabs = $module->getAdminTabs();
$fields = $module->getAdminFields();
$payload = $module->getPayload();
$instance_id = $module->getInstanceId();
todd_betFichier templates/core_admin_shell.php
<?php if (!empty($tabs)): ?>
<ul class="nav nav-tabs mb-3 todd-nav-icons-only" role="tablist">
<?php $isFirst = true; ?>
<?php foreach ($tabs as $tabId => $tab): ?>
<?php
$tabDomId = $module->getDomId("tab_" . $tabId);
$paneDomId = $module->getDomId("pane_" . $tabId);
?>
<li class="nav-item" role="presentation">
<button class="nav-link <?= $isFirst
? "active"
...
</button>
</li>
<?php $isFirst = false; ?>
<?php endforeach; ?>
</ul>
<?php endif; ?>
ToddMosaicToddMosaicToddModule suffisamment souple pour accepter des surcharges
public function renderAdmin(): void
{
$this->enqueueAdminAssets();
$module = $this;
$templatePath = BASE_PATH . 'plugins/todd/modules/todd_mosaic/views/mosaic_admin_shell.php';
if (file_exists($templatePath)) {
require $templatePath;
} else {
echo '<div class="alert alert-danger">CMS TODD Fatal Error : Vue administrative Mosaïque introuvable.</div>';
}
}
ToddMosaicToddModule suffisamment souple pour accepter des surcharges
public function enqueueAdminAssets(): void
{
// --- ASSETS JAVASCRIPT ---
$jsPath = BASE_PATH . 'plugins/todd/modules/todd_mosaic/assets/js/mosaic_admin_shell.js';
// Utilisation de programmation défensive demandée pour garantir le fonctionnement dev
$jsVersion = file_exists($jsPath) ? filemtime($jsPath) : '1.0';
App::registerJS(
"todd_mosaic_admin_js",
SITE_URL . "plugins/todd/modules/todd_mosaic/assets/js/mosaic_admin_shell.js?v=" . $jsVersion,
false,
false
);
App::enqueueJS("todd_mosaic_admin_js");
...