WiFiManager: add DFTE root template and chunked rendering for handleRoot; introduce shared placeholder registry via server singleton; add getMenuOut(String*) overload and menu logic for params/close

This commit is contained in:
2025-11-16 22:21:47 +10:00
parent 3b488423a8
commit 3054123b7f
5 changed files with 184 additions and 20 deletions
@@ -78,6 +78,7 @@ class WiFiManagerHandlers {
String getHTTPHead(String title, String classes = "");
String getHTTPEnd();
String getMenuOut();
String getMenuOut(String* outOpt);
String getScanItemOut();
String getParamOut();
String getStaticOut();
@@ -19,6 +19,9 @@
#include <functional>
#include <memory>
// DFTE (template engine)
#include <TemplateEngine.h>
// Forward declarations
class WiFiManager;
@@ -46,8 +49,21 @@ const char R_updatedone[] PROGMEM = "/u";
class WiFiManagerServer {
public:
// Singleton access
static WiFiManagerServer* instance();
WiFiManagerServer(WiFiManager* wm);
// Template engine setup and access
void setupTemplateEngine();
PlaceholderRegistry* getPlaceholderRegistry() { return _tplRegistry.get(); }
// Static zero-arg getters for DFTE RAM placeholders
static const char* tplGetPageTitle();
static const char* tplGetSubtitle();
static const char* tplGetMenu();
static const char* tplGetStatus();
// Create server instance
void createServer(uint16_t port);
@@ -74,6 +90,12 @@ class WiFiManagerServer {
std::unique_ptr<WiFiManagerHandlers> _handlers;
std::unique_ptr<AsyncWebServer> server;
std::unique_ptr<DNSServer> dnsServer;
// Shared DFTE placeholder registry
std::unique_ptr<PlaceholderRegistry> _tplRegistry;
// Singleton instance
static WiFiManagerServer* s_instance;
};
// Include WiFiManagerHandlers.h after class definition to provide full definition
+38
View File
@@ -0,0 +1,38 @@
/**
* Root.h
* Whole-document root template for WiFiManager using DFTE placeholders
*/
#ifndef _WM_ROOT_TEMPLATE_H_
#define _WM_ROOT_TEMPLATE_H_
#include <Arduino.h>
// Whole document template:
// - Injects %SCRIPTS% and %STYLES% (these already include <script>/<style> tags)
// - Mirrors existing structure: body class "home" and wrapper div.wrap
// - Renders title/subtitle, then menu and status
const char WM_ROOT_TEMPLATE[] PROGMEM =
"<!DOCTYPE html>"
"<html lang='en'>"
"<head>"
"<meta name='format-detection' content='telephone=no'>"
"<meta charset='UTF-8'>"
"<meta name='viewport' content='width=device-width,initial-scale=1,user-scalable=no'/>"
"<title>%PAGE_TITLE%</title>"
"%SCRIPTS%"
"%STYLES%"
"</head>"
"<body class='home'>"
"<div class='wrap'>"
"<h1>%PAGE_TITLE%</h1>"
"<h3>%SUBTITLE%</h3>"
"%MENU%"
"%STATUS%"
"</div>"
"</body>"
"</html>";
#endif // _WM_ROOT_TEMPLATE_H_
+45 -20
View File
@@ -13,6 +13,8 @@
#include "templates/HTML.h"
#include "templates/CSS.h"
#include "templates/JS.h"
#include "templates/Root.h"
#include <TemplateEngine.h>
#if defined(ESP8266) || defined(ESP32)
@@ -38,13 +40,30 @@ String WiFiManagerHandlers::getHTTPEnd() {
}
String WiFiManagerHandlers::getMenuOut(){
String page;
page += HTML_PORTAL_MENU[0]; // WIFI
page += HTML_PORTAL_MENU[2]; // INFO
page += HTML_PORTAL_MENU[6]; // EXIT
page += HTML_PORTAL_MENU[9]; // SEP
page += HTML_PORTAL_MENU[8]; // UPDATE
return page;
return getMenuOut(nullptr);
}
String WiFiManagerHandlers::getMenuOut(String* outOpt){
String local;
String &out = outOpt ? *outOpt : local;
out += HTML_PORTAL_MENU[0]; // WIFI (scan)
// Show PARAM (Setup) when params are on their own page
if(!_wm->_paramsInWifi && _wm->_paramsCount > 0){
out += HTML_PORTAL_MENU[3]; // PARAM
}
out += HTML_PORTAL_MENU[2]; // INFO
// When captive/config portal is active, offer Close (keeps portal running)
if(_wm->configPortalActive){
out += HTML_PORTAL_MENU[4]; // CLOSE
}
out += HTML_PORTAL_MENU[6]; // EXIT
out += HTML_PORTAL_MENU[9]; // SEP
out += HTML_PORTAL_MENU[8]; // UPDATE
return out;
}
String WiFiManagerHandlers::getScanItemOut(){
@@ -680,19 +699,25 @@ void WiFiManagerHandlers::handleRoot(AsyncWebServerRequest *request) {
#endif
if (captivePortal(request)) return;
handleRequest(request);
String page = getHTTPHead(_wm->_title, FPSTR(C_root));
// Build root main HTML directly without tokens
page += F("<h1>");
page += _wm->_title;
page += F("</h1><h3>");
page += (_wm->configPortalActive ? _wm->_apName : (_wm->getWiFiHostname() + " - " + WiFi.localIP().toString()));
page += F("</h3>");
page += FPSTR(HTML_PORTAL_OPTIONS);
page += getMenuOut();
reportStatus(page);
page += getHTTPEnd();
request->send(200, FPSTR(HTTP_HEAD_CT), page);
// Stream-render the root page using DFTE and the shared registry
TemplateContext ctx;
if (_wm->_serverManager && _wm->_serverManager->getPlaceholderRegistry()) {
ctx.setRegistry(_wm->_serverManager->getPlaceholderRegistry());
}
TemplateRenderer::initializeContext(ctx, WM_ROOT_TEMPLATE);
// Keep context alive across chunk calls
auto ctxPtr = std::make_shared<TemplateContext>(ctx);
AsyncWebServerResponse *response = request->beginChunkedResponse(String(FPSTR(HTTP_HEAD_CT)),
[ctxPtr](uint8_t *buffer, size_t maxLen, size_t /*index*/) -> size_t {
if (!ctxPtr) return 0;
size_t written = TemplateRenderer::renderNextChunk(*ctxPtr, buffer, maxLen);
return written;
}
);
request->send(response);
if(_wm->_preloadwifiscan) _wm->WiFi_scanNetworks(_wm->_scancachetime);
}
+78
View File
@@ -11,11 +11,78 @@
#include "WiFiManagerServer.h"
#include "WiFiManager.h"
#include "WiFiManagerHandlers.h" // Full definition needed for unique_ptr
#include "templates/CSS.h"
#include "templates/JS.h"
#include "templates/Root.h"
#if defined(ESP8266) || defined(ESP32)
WiFiManagerServer* WiFiManagerServer::s_instance = nullptr;
WiFiManagerServer* WiFiManagerServer::instance() {
return s_instance;
}
WiFiManagerServer::WiFiManagerServer(WiFiManager* wm)
: _wm(wm), _handlers(std::make_unique<WiFiManagerHandlers>(wm)) {
s_instance = this;
}
// Static getters for RAM placeholders (registry requires zero-arg functions) using singleton
const char* WiFiManagerServer::tplGetPageTitle() {
auto srv = WiFiManagerServer::instance();
if (!srv || !srv->_wm) return "";
return srv->_wm->_title.c_str();
}
const char* WiFiManagerServer::tplGetSubtitle() {
static String buf;
auto srv = WiFiManagerServer::instance();
if (!srv || !srv->_wm) { buf = ""; return buf.c_str(); }
WiFiManager* wm = srv->_wm;
if (wm->configPortalActive) {
buf = wm->_apName;
} else {
buf = wm->getWiFiHostname() + " - " + WiFi.localIP().toString();
}
return buf.c_str();
}
const char* WiFiManagerServer::tplGetMenu() {
static String buf;
auto srv = WiFiManagerServer::instance();
if (!srv || !srv->_handlers) { buf = ""; return buf.c_str(); }
buf = "";
srv->_handlers->getMenuOut(&buf);
return buf.c_str();
}
const char* WiFiManagerServer::tplGetStatus() {
static String buf;
auto srv = WiFiManagerServer::instance();
if (!srv || !srv->_handlers) { buf = ""; return buf.c_str(); }
buf = "";
srv->_handlers->reportStatus(buf);
return buf.c_str();
}
void WiFiManagerServer::setupTemplateEngine() {
// Initialize shared placeholder registry once
if (!_tplRegistry) {
_tplRegistry = std::unique_ptr<PlaceholderRegistry>(new PlaceholderRegistry(12));
} else {
_tplRegistry->clear();
}
// Register PROGMEM assets
_tplRegistry->registerProgmemData("%STYLES%", CSS_STYLE);
_tplRegistry->registerProgmemData("%SCRIPTS%", JS_SCRIPT);
// Register dynamic RAM getters
_tplRegistry->registerRamData("%PAGE_TITLE%", &WiFiManagerServer::tplGetPageTitle);
_tplRegistry->registerRamData("%SUBTITLE%", &WiFiManagerServer::tplGetSubtitle);
_tplRegistry->registerRamData("%MENU%", &WiFiManagerServer::tplGetMenu);
_tplRegistry->registerRamData("%STATUS%", &WiFiManagerServer::tplGetStatus);
}
void WiFiManagerServer::createServer(uint16_t port) {
@@ -38,6 +105,9 @@ void WiFiManagerServer::createServer(uint16_t port) {
}
server.reset(new AsyncWebServer(port));
// Ensure template engine is ready whenever a server is (re)created
setupTemplateEngine();
}
void WiFiManagerServer::registerRoutes() {
@@ -48,6 +118,11 @@ void WiFiManagerServer::registerRoutes() {
return;
}
// Ensure template engine is initialized before routes use it
if (!_tplRegistry) {
setupTemplateEngine();
}
if (_wm->_webservercallback != NULL) {
#ifdef WM_DEBUG_LEVEL
_wm->DEBUG_WM(WM_DEBUG_VERBOSE, F("[CB] _webservercallback calling"));
@@ -164,6 +239,9 @@ void WiFiManagerServer::shutdownServer() {
dnsServer->stop(); // free heap ?
dnsServer.reset();
}
// Clear singleton on shutdown
if (s_instance == this) s_instance = nullptr;
}
#endif