Se vi è mai capitato di passare 30 minuti a "smanettare" con uno shortcode solo per poi rendervi conto che è impossibile personalizzarlo correttamente nel Visual Builder, allora avete già sentito il bisogno di un vero modulo per Divi 5.

Il problema / Il bisogno

Un'esigenza comune per blogger e team editoriali: visualizzare un riquadro riutilizzabile (ad esempio, "Nota dell'autore", "Disclaimer", "Lista di controllo", "CTA per la newsletter") con opzioni semplici (titolo, testo, icona, colore, link), mantenendo al contempo:

  • una modifica visiva in Divi 5 (non un campo “shortcode” opaco),
  • stili coerenti con il tuo sistema di progettazione,
  • rendering efficiente (senza JavaScript superfluo),
  • codice gestibile (senza frammenti sparsi).

Alla fine, saprai come creare un plugin WordPress Questo programma registra un modulo Divi 5 personalizzato tramite l'API dei moduli, aggiunge campi, genera l'HTML lato server (PHP) e carica il CSS pulito. Riceverai anche un metodo di test e una checklist realistica per la risoluzione dei problemi.

Riassunto veloce

  • Creiamo un plugin "silenzioso" sul lato dell'interfaccia utente. WordPress, che registra solo un modulo Divi 5.
  • Definiamo i campi del modulo (titolo, contenuto, stile, link) e i relativi valori predefiniti.
  • Rendiamo il modulo disponibile in PHP con opportune procedure di escape/sanificazione (WordPress 6.9.4 + PHP 8.1+).
  • Carichiamo un CSS minimo tramite wp_enqueue_scripts (e in questo modo evitiamo query globali non necessarie).
  • Abbiamo in programma di includere delle misure di sicurezza: verifica dell'attivazione di Divi 5, compatibilità della cache, gestione degli errori frequenti.

Quando utilizzare questa soluzione

  • Hai un sito web che utilizza Divi 5 e desideri un componente (modulo) riutilizzabile e "pulito" anziché uno shortcode.
  • È necessario fornire al team delle opzioni editoriali (colori, varianti, link) senza però permettere loro di stravolgere il layout.
  • Hai diversi siti Divi e desideri impacchettare un modulo in un plugin riutilizzabile e versionato (Git).
  • Desideri un rendering stabile sul front-end (PHP) e non una logica frammentata tra shortcode, widget e CSS inline.

Quando NON utilizzare questa soluzione

  • Se non stai usando Divi 5 (o stai ancora migrando da Divi 4): inizia stabilizzando il builder. Un modulo di Divi 5 non sarà d'aiuto a un sito che utilizza ancora uno stack ibrido.
  • Ciò di cui hai bisogno è un semplice blocco di contenuto statico: un Biblioteca Divi (Il layout salvato) è spesso sufficiente.
  • Hai bisogno di un componente universale (Gutenberg, Elementor, Avada, Divi): dai la priorità a un blocco Gutenberg + stili, poi aggiungi le integrazioni specifiche se necessario.
  • Devi visualizzare dati altamente dinamici (API esterne, ricerca, filtri): un modulo potrebbe essere adatto, ma dovrai considerare la cache, i timeout e possibilmente un endpoint REST. Non iniziare con un modulo se il tuo problema principale sono i dati.

Prerequisiti / prima di iniziare

Contesto di destinazione: WordPress 6.9.4 (aprile 2026) e PHP 8.1+. Non testare su PHP 7.x: perderai tempo con errori di sintassi e dipendenze.

  • Un ambiente di sviluppo locale : WP-CLI + un sito di prova (LocalWP, DDEV, Docker... a seconda delle preferenze).
  • Divi 5 installato e attivo (ultima versione stabile).
  • Accesso FTP/SSH o un file manager per caricare un plugin.
  • Un backup (almeno file + base) se stai modificando un sito esistente.

Strumenti utili:

Sicurezza: un modulo Divi può visualizzare contenuti multimediali. Il rischio principale che riscontro durante le verifiche è l'introduzione di codice HTML non filtrato (XSS) tramite un campo di testo gestito in modo inadeguato. Pertanto, dovrai scegliere esplicitamente cosa consentire (testo semplice o HTML).

L'approccio ingenuo (e perché evitarlo)

Ciò che vedo spesso sui siti Divi: uno shortcode in functions.phpPoi un modulo "Codice" di Divi che incolla lo shortcode, e infine del CSS in "Opzioni tema > CSS personalizzato". Funziona... fino al giorno in cui:

  • Il tema viene aggiornato e il codice scompare (se non è presente un tema figlio).
  • Un editor interrompe lo shortcode (virgolette, attributi),
  • Il CSS diventa ingestibile (specificità, collisioni),
  • Abbiamo riscontrato una vulnerabilità XSS tramite un attributo non sottoposto a escape.

Esempio ingenuo (non riprodurre):

<?php
// MAUVAIS EXEMPLE : shortcode + HTML non échappé + insertion dans un module Code.
add_shortcode('author_note', function($atts, $content = '') {
    $atts = shortcode_atts([
        'title' => 'Note',
        'color' => '#ffcc00',
    ], $atts);

    // Problème : $atts['title'] et $content sortent tels quels => XSS possible.
    return '<div class="author-note" style="border-color:' . $atts['color'] . '">
        <h4>' . $atts['title'] . '</h4>
        <div>' . $content . '</div>
    </div>';
});

Il problema deriva da due fattori: la mancanza di escape/sanificazione e la mancanza di integrazione nativa nel builder (nessuna interfaccia utente, nessuna anteprima pulita, nessun stile versionato).

L'approccio corretto: tutorial passo passo

Obiettivo: un modulo "Inserimento editoriale" (titolo + testo + variante + link) utilizzabile in Divi 5, renderizzato lato server in PHP, con CSS caricato correttamente.

Passaggio 1: crea un plugin dedicato (non in functions.php)

Crea una cartella:

wp-content/plugins/bpcab-divi5-module/

Quindi un file principale:

wp-content/plugins/bpcab-divi5-module/bpcab-divi5-module.php

Dopodiché, attivalo nel pannello di amministrazione. Su un sito web professionale, per questo tipo di funzionalità preferisco un plugin a un tema figlio: è portatile e versionabile.

Passaggio 2: aggiungi un rilevamento "Divi 5 attivo".

Divi carica le sue classi e le API dei moduli solo in determinati contesti. Se registri il modulo troppo presto, riceverai errori del tipo "classe non trovata". L'idea è di registrare il modulo solo se Divi è presente, evitando così di compromettere il funzionamento del front-end se Divi è disabilitato.

Non pretendo che esista una funzione universale "is_divi_5()": a seconda della versione, i punti di ingresso cambiano. L'approccio più affidabile che utilizzo è quello di verificare l'esistenza di classi/funzioni Divi al momento opportuno e, in caso contrario, non fare nulla (fail-safe).

Passaggio 3: dichiara il modulo tramite l'API dei moduli di Divi 5.

Divi 5 introduce un'API per i moduli più moderna (più simile a un sistema di registrazione dei "componenti"). A seconda della build, la registrazione può essere effettuata tramite una classe PHP o tramite un manifest + JavaScript. Qui, puntiamo a un modulo semplice e affidabile: rendering PHP, campi di base.

In pratica, Elegant Themes evolve costantemente la sua API. Il mio consiglio: partite dalla struttura ufficiale della loro "API dei moduli" e adattatela. Mantenete il vostro codice incapsulato (namespace, classi) per assorbire i cambiamenti.

Passaggio 4 — Rendering HTML lato server (PHP) con escape corretto

Per un inserto editoriale:

  • Il titolo: testo semplice (sanitize_text_field).
  • Il contenuto: testo semplice o HTML filtrato (wp_kses_postIn un builder, gli utenti spesso si aspettano un livello minimo di formattazione, quindi wp_kses_post è un buon compromesso.
  • Il link: esc_url + convalida.
  • Classi CSS: whitelist (nessuna concatenazione non consentita).

Passaggio 5: Carica il CSS senza rallentare tutte le pagine

Idealmente, dovresti caricare il CSS solo se il modulo è presente. Divi ha i suoi meccanismi condizionali per le risorse, ma variano. Ecco una versione "robusta": inserisci un CSS leggero a livello globale (poche righe) oppure in modo condizionale se disponi di un hook affidabile lato Divi.

Sui siti con sistemi di caching aggressivi (LiteSpeed, WP Rocket), ho spesso notato che alcuni stili "non vengono visualizzati" perché la risorsa viene caricata troppo tardi o perché l'anteprima del builder ha un contesto diverso. Quindi opteremo per una soluzione semplice e stabile.

Codice completo

Il codice seguente rappresenta un plugin completo. Puoi copiarlo e incollarlo così com'è. È stato scritto per WordPress 6.9.4 e PHP 8.1+.

fichier: wp-content/plugins/bpcab-divi5-module/bpcab-divi5-module.php

<?php
/**
 * Plugin Name: BPCAB - Module Divi 5 (Encart éditorial)
 * Description: Ajoute un module custom Divi 5 "Encart éditorial" (titre, contenu, variante, lien) avec rendu PHP.
 * Version: 1.0.0
 * Author: BPCAB
 * Requires at least: 6.9
 * Requires PHP: 8.1
 */

declare(strict_types=1);

namespace BPCABDivi5Module;

if (!defined('ABSPATH')) {
	exit;
}

final class Plugin {
	public const VERSION = '1.0.0';
	public const SLUG    = 'bpcab-divi5-module';

	public static function init(): void {
		add_action('plugins_loaded', [__CLASS__, 'bootstrap'], 20);
		add_action('wp_enqueue_scripts', [__CLASS__, 'enqueue_assets'], 20);
	}

	/**
	 * Bootstrap : enregistre le module uniquement si Divi 5 (ou l'API module attendue) est disponible.
	 */
	public static function bootstrap(): void {
		// Garde-fou : si Divi n'est pas actif, on ne casse rien.
		if (!self::is_divi_module_api_available()) {
			return;
		}

		/**
		 * NOTE:
		 * Divi 5 a fait évoluer ses hooks et points d'entrée pendant la période alpha/beta.
		 * L'approche la plus sûre est d'accrocher l'enregistrement au hook recommandé par Divi
		 * si disponible. À défaut, on tente une initialisation tardive.
		 */
		if (has_action('et_builder_ready')) {
			// Divi expose souvent un hook "ready" (selon versions).
			add_action('et_builder_ready', [__CLASS__, 'register_module']);
		} else {
			// Fallback : on enregistre tard, après init.
			add_action('init', [__CLASS__, 'register_module'], 99);
		}
	}

	/**
	 * Détecte la disponibilité de l'API module Divi.
	 * On évite d'inventer un "helper" : on teste des symboles réellement chargés.
	 */
	private static function is_divi_module_api_available(): bool {
		// Selon les versions, Divi peut exposer des classes côté Builder.
		// Ajustez ces tests si Elegant Themes renomme les namespaces.
		if (class_exists('\ET_Builder_Element')) {
			return true;
		}

		// Dans certains builds, l'API est exposée autrement (fonction/loader).
		if (function_exists('et_builder_init_global_settings')) {
			return true;
		}

		return false;
	}

	/**
	 * Enregistre le module.
	 * IMPORTANT : ce code est conçu pour rester "fail safe" : si une classe Divi manque, on n'explose pas.
	 */
	public static function register_module(): void {
		// Si la classe de base Divi n'existe pas, on stoppe.
		if (!class_exists('\ET_Builder_Module')) {
			return;
		}

		// Déclaration de la classe du module dans le bon scope.
		if (!class_exists(__NAMESPACE__ . '\EditorialBoxModule')) {
			require_once __DIR__ . '/includes/class-editorial-box-module.php';
		}

		// Enregistrement Divi (Divi 4 utilisait et_builder_module_register, Divi 5 peut varier).
		// On tente l'approche la plus compatible : instanciation.
		if (class_exists(__NAMESPACE__ . '\EditorialBoxModule')) {
			new EditorialBoxModule();
		}
	}

	/**
	 * CSS léger pour le rendu front.
	 * Conseil : gardez ce fichier petit. Le gros du styling doit rester dans Divi (Design tab) ou un CSS global.
	 */
	public static function enqueue_assets(): void {
		$css_file = __DIR__ . '/assets/editorial-box.css';
		$css_url  = plugin_dir_url(__FILE__) . 'assets/editorial-box.css';

		if (file_exists($css_file)) {
			wp_enqueue_style(
				self::SLUG,
				$css_url,
				[],
				self::VERSION
			);
		}
	}
}

Plugin::init();

fichier: wp-content/plugins/bpcab-divi5-module/includes/class-editorial-box-module.php

<?php
declare(strict_types=1);

namespace BPCABDivi5Module;

if (!defined('ABSPATH')) {
	exit;
}

/**
 * Module Divi "Encart éditorial".
 *
 * Ce module est volontairement simple :
 * - champs : titre, contenu, variante, lien
 * - rendu PHP (shortcode_callback) pour un output stable et facilement testable
 */
final class EditorialBoxModule extends ET_Builder_Module {

	public $slug       = 'bpcab_editorial_box';
	public $vb_support = 'on';

	protected $module_credits = [
		'module_uri' => '',
		'author'     => 'BPCAB',
		'author_uri' => '',
	];

	public function init(): void {
		$this->name = esc_html__('Encart éditorial (BPCAB)', 'bpcab-divi5-module');
		$this->icon_path = ''; // Optionnel : icône SVG.
	}

	/**
	 * Définition des champs affichés dans Divi Builder.
	 */
	public function get_fields(): array {
		return [
			'title' => [
				'label'           => esc_html__('Titre', 'bpcab-divi5-module'),
				'type'            => 'text',
				'option_category' => 'basic_option',
				'toggle_slug'     => 'main_content',
				'default'         => esc_html__('À retenir', 'bpcab-divi5-module'),
				'description'     => esc_html__('Titre affiché en haut de l’encart.', 'bpcab-divi5-module'),
			],
			'content' => [
				'label'           => esc_html__('Contenu', 'bpcab-divi5-module'),
				'type'            => 'tiny_mce',
				'option_category' => 'basic_option',
				'toggle_slug'     => 'main_content',
				'default'         => esc_html__('Votre texte ici…', 'bpcab-divi5-module'),
				'description'     => esc_html__('Texte de l’encart (HTML basique autorisé).', 'bpcab-divi5-module'),
			],
			'variant' => [
				'label'           => esc_html__('Variante', 'bpcab-divi5-module'),
				'type'            => 'select',
				'option_category' => 'configuration',
				'toggle_slug'     => 'settings',
				'default'         => 'info',
				'options'         => [
					'info'    => esc_html__('Info', 'bpcab-divi5-module'),
					'warning' => esc_html__('Avertissement', 'bpcab-divi5-module'),
					'tip'     => esc_html__('Astuce', 'bpcab-divi5-module'),
				],
				'description'     => esc_html__('Change la classe CSS pour adapter le style.', 'bpcab-divi5-module'),
			],
			'link_url' => [
				'label'           => esc_html__('Lien (URL)', 'bpcab-divi5-module'),
				'type'            => 'text',
				'option_category' => 'basic_option',
				'toggle_slug'     => 'link',
				'default'         => '',
				'description'     => esc_html__('Optionnel. Ajoute un bouton “En savoir plus”.', 'bpcab-divi5-module'),
			],
			'link_label' => [
				'label'           => esc_html__('Texte du lien', 'bpcab-divi5-module'),
				'type'            => 'text',
				'option_category' => 'basic_option',
				'toggle_slug'     => 'link',
				'default'         => esc_html__('En savoir plus', 'bpcab-divi5-module'),
				'description'     => esc_html__('Libellé du bouton.', 'bpcab-divi5-module'),
			],
		];
	}

	/**
	 * Rendu HTML du module.
	 * Divi appelle cette méthode pour produire le front.
	 */
	public function render($attrs, $content = null, $render_slug = ''): string {
		// Récupération des champs.
		$title      = (string) ($this->props['title'] ?? '');
		$body_html  = (string) ($this->props['content'] ?? '');
		$variant    = (string) ($this->props['variant'] ?? 'info');
		$link_url   = (string) ($this->props['link_url'] ?? '');
		$link_label = (string) ($this->props['link_label'] ?? '');

		// Sanitation.
		$title = sanitize_text_field($title);

		// Divi peut injecter des balises et des shortcodes via tinyMCE.
		// On autorise un HTML "post" standard.
		$body_html = wp_kses_post($body_html);

		// Whitelist stricte sur la variante (évite injection de classes).
		$allowed_variants = ['info', 'warning', 'tip'];
		if (!in_array($variant, $allowed_variants, true)) {
			$variant = 'info';
		}

		$link_url = trim($link_url);
		if ($link_url !== '') {
			$link_url = esc_url_raw($link_url);
			// Si esc_url_raw retourne vide, l'URL est invalide.
			if ($link_url === '') {
				$link_url = '';
			}
		}

		$link_label = sanitize_text_field($link_label);
		if ($link_label === '') {
			$link_label = esc_html__('En savoir plus', 'bpcab-divi5-module');
		}

		// Construction du HTML.
		$classes = [
			'bpcab-editorial-box',
			'bpcab-editorial-box--' . $variant,
		];

		$button_html = '';
		if ($link_url !== '') {
			$button_html = sprintf(
				'<p class="bpcab-editorial-box__cta"><a class="bpcab-editorial-box__button" href="%s">%s</a></p>',
				esc_url($link_url),
				esc_html($link_label)
			);
		}

		// Note : on échappe les attributs et on laisse le body filtré par wp_kses_post.
		$output  = '<div class="' . esc_attr(implode(' ', $classes)) . '">';
		$output .= '<div class="bpcab-editorial-box__inner">';
		if ($title !== '') {
			$output .= '<h3 class="bpcab-editorial-box__title">' . esc_html($title) . '</h3>';
		}
		if ($body_html !== '') {
			$output .= '<div class="bpcab-editorial-box__content">' . $body_html . '</div>';
		}
		$output .= $button_html;
		$output .= '</div></div>';

		return $output;
	}
}

fichier: wp-content/plugins/bpcab-divi5-module/assets/editorial-box.css

/* CSS minimal : gardez-le léger, le reste peut être géré par Divi (Design tab) */
.bpcab-editorial-box {
  border: 1px solid rgba(0,0,0,.12);
  border-left-width: 6px;
  padding: 16px;
  border-radius: 10px;
  background: #fff;
}

.bpcab-editorial-box__title {
  margin: 0 0 10px 0;
}

.bpcab-editorial-box__content > :first-child {
  margin-top: 0;
}

.bpcab-editorial-box__cta {
  margin-top: 12px;
}

.bpcab-editorial-box__button {
  display: inline-block;
  padding: 10px 14px;
  border-radius: 8px;
  text-decoration: none;
  border: 1px solid rgba(0,0,0,.12);
}

/* Variantes */
.bpcab-editorial-box--info { border-left-color: #2b6cb0; }
.bpcab-editorial-box--warning { border-left-color: #b7791f; }
.bpcab-editorial-box--tip { border-left-color: #2f855a; }

Codice Spiegazione

Perché un plugin e non uno snippet?

Divi 5 carica parte del suo runtime builder in modo condizionale. Un frammento di codice incollato in un plugin "Code Snippets" a volte funziona... poi smette di funzionare dopo un aggiornamento o nel Visual Builder, perché l'ordine di caricamento cambia. Un plugin dedicato fornisce:

  • un punto di ingresso chiaro (plugins_loaded),
  • file separati (modulo, risorse),
  • una semplice gestione delle versioni
  • una disattivazione pulita.

Rilevamento API Divi

Il metodo is_divi_module_api_available() Divi non "indovina"; verifica l'esistenza dei simboli. Questa scelta è intenzionalmente prudente. Nella mia esperienza, è ciò che previene errori fatali quando:

  • Divi è disabilitato.
  • Un ambiente di staging non ha la stessa versione,
  • Un plugin di caching/minificazione modifica il carico del builder.

campi componenti

get_fields() dichiara i campi tipici di Divi:

  • testo per valori semplici (titolo, descrizione),
  • tiny_mce per contenuti ricchi (con successivo filtraggio),
  • select per una variante (e quindi una lista bianca).

Il punto chiave: la whitelist su variantSenza di esso, si finisce per avere classi CSS iniettate e bug che sono "impossibili" da riprodurre.

Igiene e fuga (WordPress 6.9.4)

Regola semplice: noi igienizzare all'input logico (valori) e noi fuga in uscita (HTML).

  • sanitize_text_field() per titolo/etichetta (testo semplice).
  • wp_kses_post() per contenuti di testo formattato (consente un sottoinsieme di tag).
  • esc_url_raw() per convalidare/memorizzare un URL, quindi esc_url() al momento della stampa.
  • esc_attr() per classi/attributi.

Documento ufficiale: Sanificazione dei dati et Dati in fuga.

Caricamento CSS

Noi usiamo wp_enqueue_style con una versione plugin per facilitare l'invalidazione della cache. Riferimento: wp_enqueue_style().

Sì, questo CSS viene caricato su tutte le pagine. Nell'uso pratico, questo è spesso accettabile se il file ha una dimensione di 1-2 KB. Se si desidera applicarlo in modo condizionale al modulo (il che è più complesso), fornirò una variante qui di seguito.

Varianti e casi d'uso

Variante 1 — CSS condizionale solo se il modulo è presente

Se sei ossessionato (giustamente) dalle prestazioni, puoi caricare il CSS solo quando il modulo viene renderizzato. Il modello: accoda al volo in render()Funziona, ma bisogna fare attenzione alle cache e a determinati contesti di compilazione.

<?php
// À placer dans render(), avant de retourner $output.
if (!wp_style_is(Plugin::SLUG, 'enqueued')) {
	wp_enqueue_style(
		Plugin::SLUG,
		plugin_dir_url(dirname(__FILE__, 2) . '/bpcab-divi5-module.php') . 'assets/editorial-box.css',
		[],
		Plugin::VERSION
	);
}

Lo utilizzo principalmente quando il CSS è pesante. Per un modulo "insert", mantengo l'enqueue globale, che è più affidabile.

Variante 2 — Aggiungere un'opzione "icona" (SVG) rimanendo sicuri

Non lasciare un campo "HTML libero" per un'icona. Utilizza invece un elenco di icone (slug) e associale a un file SVG noto.

<?php
// Exemple de whitelist d'icônes.
$allowed_icons = [
	'info' => '<svg aria-hidden="true" viewBox="0 0 24 24"><path d="..." /></svg>',
	'tip'  => '<svg aria-hidden="true" viewBox="0 0 24 24"><path d="..." /></svg>',
];

$icon_slug = (string) ($this->props['icon'] ?? 'info');
if (!isset($allowed_icons[$icon_slug])) {
	$icon_slug = 'info';
}

// IMPORTANT : SVG inline = surface XSS si non maîtrisé.
// Ici, on n'accepte que des SVG codés en dur côté dev.
$icon_html = $allowed_icons[$icon_slug];

Opzione 3: Recupera automaticamente l'autore e la data (nella sezione "Nota dell'autore").

Una pratica comune nei blog: mostrare un inserto contestualizzato a seconda dell'articolo.

<?php
$post_id = get_the_ID();
$author_id = (int) get_post_field('post_author', $post_id);
$author_name = $author_id ? get_the_author_meta('display_name', $author_id) : '';

$meta_html = '';
if ($author_name !== '') {
	$meta_html = '<p class="bpcab-editorial-box__meta">'
		. esc_html(sprintf(__('Par %s', 'bpcab-divi5-module'), $author_name))
		. '</p>';
}

// Puis injectez $meta_html dans $output, à l’endroit voulu.

Prestazione : get_post_field et get_the_author_meta Sono leggeri, ma se hai 10 moduli per pagina, tieni d'occhio le chiamate ripetute (cache degli oggetti, variabili statiche).

Compatibilità con Divi 5, Elementor e Avada

Questo modulo è nativo di Divi 5. Se il tuo sito utilizza diversi builder (cosa che accade durante i restyling), hai tre strategie a disposizione.

Divi 5

  • OK: il modulo compare nell'elenco dei moduli e viene configurato tramite i suoi campi.
  • Se utilizzi Divi Theme Builder, il modulo rimane utilizzabile nei template.
  • Se il Visual Builder non mostra il CSS: testa senza cache/minificazione, forza l'invalidazione (vedi la sezione Risoluzione dei problemi).

Elementor

Elementor non è in grado di "leggere" un modulo Divi. L'equivalente pulito lato Elementor è un widget personalizzato (PHP + JS) o, più semplicemente, uno shortcode stabile + un widget "Shortcode". Se si desidera un bridge minimale, è possibile esporre uno shortcode che riutilizzi lo stesso renderer.

Esempio: aggiunta di uno shortcode che richiama una funzione comune (stesso HTML, stesse classi):

<?php
// Dans votre plugin, ajoutez un renderer commun et exposez-le en shortcode pour Elementor/Avada.
add_shortcode('bpcab_editorial_box', function($atts, $content = '') {
	$atts = shortcode_atts([
		'title' => 'À retenir',
		'variant' => 'info',
		'link_url' => '',
		'link_label' => 'En savoir plus',
	], $atts);

	// Sanitation/escaping identiques à ceux du module.
	// Retournez le même HTML que render().
	return '<div class="bpcab-editorial-box bpcab-editorial-box--info">...</div>';
});

Attenzione: se procedete in questo modo, mantenete un solo punto in cui viene generato l'HTML (altrimenti le divergenze saranno inevitabili).

Avada (Fusion Builder)

Vale la stessa logica di Elementor: Avada non utilizza i moduli di Divi. Puoi:

  • utilizzare un codice breve comune,
  • oppure creare un elemento Fusion Builder (la cui manutenzione richiede più tempo).

Se il tuo obiettivo è la portabilità tra diversi builder, spesso consiglio: blocco Gutenberg + stili + modelli, e poi integrazioni "opzionali" con altri builder.

Controlli post-installazione

  1. Attiva il plugin.
  2. Apri una pagina con Divi 5 Builder.
  3. Aggiungi il modulo “Inserimento editoriale (BPCAB)”.
  4. Verificare :
    • Il titolo viene visualizzato,
    • Il contenuto di TinyMCE ha un bell'aspetto (link, testo in grassetto, elenchi).
    • La variante modifica il bordo.
    • Il pulsante viene visualizzato solo se viene fornito un URL.
  5. Eseguire i test sul front-end (non solo nell'ambiente di sviluppo).

Aggiungere anche un controllo tecnico:

  • in HTML: presenza di class="bpcab-editorial-box bpcab-editorial-box--info",
  • nella scheda Rete: il CSS editorial-box.css è caricato (HTTP 200, non 404).

Se non funziona

Procedura che applico quando un modulo non viene visualizzato o non visualizza nulla.

1) Verificare la posizione del file

  • Il plugin deve essere in wp-content/plugins/bpcab-divi5-module/.
  • Il file di classe deve essere in includes/class-editorial-box-module.php.
  • CSS in assets/editorial-box.css.

Una causa comune: il file decompresso crea una cartella duplicata dello stile bpcab-divi5-module/bpcab-divi5-module/....

2) Abilita WP_DEBUG nell'ambiente di staging

Segui le istruzioni: Debug in WordPressAspetto:

  • wp-content/debug.log
  • Errori irreversibili come "Classe ET_Builder_Module non trovata" (Divi non caricato o hook troppo presto)

3) Controlla la versione di PHP

Se stai utilizzando PHP 8.0 o versioni precedenti, potresti riscontrare errori relativi a declare(strict_types=1) o alle tipologie. Riferimento: Versioni di PHP supportate.

4) Memorizzazione nella cache / Minimizzazione

Ho riscontrato spesso questo problema su siti con una minificazione CSS/JS aggressiva:

  • svuota la cache del plugin.
  • svuotare la cache del server/CDN.
  • Esegui un aggiornamento forzato del browser.

5) Collegamenti permanenti

Se riscontri comportamenti anomali dopo la migrazione, rigenera i permalink (senza modificarli): "Impostazioni > Permalink > Salva".

Tabella diagnostica

sintomo Probabile causa Verifica Soluzione
Il modulo non compare in Divi Builder Divi 5 non attivo o registrazione troppo anticipata Registri: classe ET_Builder_Module non trovato Verifica che Divi sia attivo, mantieni il record attivo et_builder_ready se disponibile, altrimenti init in ritardo
Il modulo viene visualizzato ma non mostra nulla sul front-end. Errore PHP silenzioso o contenuto filtrato permettere WP_DEBUG_LOGispeziona l'HTML Corretto l'errore, dai un'occhiata wp_kses_post e campi vuoti
Il CSS non si applica CSS non caricato (404) o cache Scheda Rete, stato del file CSS Verifica il percorso, svuota la cache e disabilita la minimizzazione per effettuare dei test.
Il pulsante "Scopri di più" non compare mai URL non valido da esc_url_raw Prova un URL completo con https:// Correggi l'URL, evita gli spazi, controlla il campo Divi
"Errore di analisi" dopo il copia-incolla Punto e virgola/parentesi mancante L'errore indica una riga specifica Copia nuovamente il file, confrontalo e usa un editor con PHP lint.

Errori e trappole comuni

errore Causare Soluzione
Il codice viene incollato in functions.php del tema principale Aggiornamento del tema = perdita di codice Utilizza un plugin (come questo) o un tema figlio, ma per un modulo è preferibile un plugin.
Fatal error: Class "ET_Builder_Module" not found Divi non è attivo o il modulo è stato registrato troppo presto Salva dopo aver caricato Divi (hook “ready” se presente) e mantieni un class_exists prima di qualsiasi estensione
Il modulo presenta problemi solo nell'editor visuale. Contesto di caricamento diverso + cache/minimizzazione Disattiva temporaneamente l'ottimizzazione, esegui un test in modalità di navigazione privata e poi riattivala gradualmente.
Il CSS/JS non viene caricato cattivo plugin_dir_url / percorso del file oppure il file è mancante Controlla l'URL nella scheda Rete, controlla la struttura delle directory
Contenuto “mangiato” (tag rimossi) wp_kses_post filtra determinati tag/attributi Accetta il vincolo (sicurezza) o estendi la whitelist tramite wp_kses_allowed_html (con cautela)
Un editor inietta codice HTML pericoloso tramite un campo Campo filtrato in modo inadeguato / output non codificato il testo: sanitize_text_fieldHTML: wp_kses_postAttributi: esc_attrURL: esc_url
Il modulo scompare dopo un aggiornamento di Divi Modulo API modificato (in arrivo) Incapsula il tuo codice, evita soluzioni improvvisate, segui la documentazione ufficiale di Divi 5 e adatta i tuoi hook/punti di ingresso.
Testato direttamente in produzione Rischio di una pagina bianca in caso di errore irreversibile. Distribuzione con staging iniziale, backup e versione.

Consigli su sicurezza, prestazioni e manutenzione

sicurezza

  • Evitate i campi "HTML libero" se potete sostituirli con delle opzioni (select, toggle, ecc.).
  • Se devi accettare HTML, wp_kses_post è un buon standard. Riferimento: wp_kses_post().
  • Non stampare mai un URL senza esc_url. Riferimento : esc_url().

Cookie di prestazione

  • Mantieni il tuo CSS conciso. Se il tuo modulo diventa un "mini framework", ne pagherai le conseguenze su ogni pagina.
  • Evitare richieste aggiuntive in render()Se è necessario caricare i metadati, utilizzare le cache (transient/object cache) quando opportuno. Riferimento: API Transienti.
  • Prova con la cache abilitata: alcuni problemi si presentano solo con concat/minify.

Manutenzione

  • Gestisci la versione del plugin (Git) e assegna un tag alle tue release.
  • Se il sito è multilingue, aggiungi un dominio di testo e preparati all'internazionalizzazione.
  • Monitora le modifiche di Divi 5: l'API dei moduli potrebbe evolversi. Mantieni il tuo codice isolato in includes/ ed evitare dipendenze implicite.

Risorse

FAQ

Questo plugin è compatibile con WordPress 6.9.4?

Sì. Il codice utilizza API stabili (hook, enqueue, sanitation/escaping) compatibili con WordPress 6.9.4 e PHP 8.1+.

Perché il mio modulo non viene visualizzato in Divi 5?

Nell'80% dei casi: Divi non è attivo nell'ambiente oppure l'API del modulo non viene caricata quando si salva il modulo. Controlla i log e assicurati che ET_Builder_Module esiste prima di estendere la classe.

Perché non usi un singolo hook "ufficiale" di Divi?

Poiché Divi 5 si è evoluto rapidamente e a seconda della build, l'hook esatto può cambiare. Il codice è intenzionalmente difensivo: tenta un hook "ready" se esiste; altrimenti, si registra in seguito tramite init.

Posso aggiungere le opzioni di "Design" di Divi (colori, tipografia)?

Sì, ma dipende dalle API di Divi 5 disponibili nella tua versione. Il mio consiglio: inizia con un modulo "content/settings" stabile, poi aggiungi i controlli di design dopo aver verificato il rendering front-end e la compatibilità con il builder.

È possibile memorizzare i valori del modulo nel database?

Divi memorizza la configurazione all'interno del contenuto della pagina (tramite lo Structure Builder). Non è necessario gestire una tabella personalizzata per questo scopo.

Come evitare di caricare i CSS ovunque?

Puoi fare la fila render() (variante sopra). Quindi, prova con la cache/minimizzazione, poiché alcuni strumenti aggregano le risorse in modo diverso tra il front-end e il builder.

Posso riutilizzare lo stesso componente in Elementor e Avada?

Non direttamente. Un modulo Divi non è un widget Elementor. Il collegamento più semplice è un renderer comune + uno shortcode, dopodiché ogni builder inserisce lo shortcode (con le limitazioni che ciò implica in termini di esperienza utente).

Perché filtrare i contenuti con wp_kses_post ?

Perché riduce significativamente il rischio di XSS pur consentendo l'utilizzo di HTML editoriale standard. Se si consentono ulteriori funzionalità (SVG, script), si aumenta la superficie di attacco.

Vedo errori 404 su editorial-box.css

Percorso errato o file mancante. Verificare la struttura delle directory e assicurarsi che il file si trovi nella posizione corretta. assets/Dopo aver decompresso il file, verificate anche la presenza di cartelle duplicate.

Il modulo funziona sul front-end ma non in Visual Builder.

Disabilita temporaneamente la minimizzazione/ottimizzazione, svuota la cache e prova con un tema minimale. Se funziona, riattiva le ottimizzazioni una alla volta. Questo è uno schema molto comune su Stack Overflow con ottimizzazioni aggressive.

Posso inserire questo codice in un plugin per snippet?

Tecnicamente sì, ma preferisco evitarlo. Quando Divi cambia l'ordine di caricamento, diventa difficile diagnosticare i bug. Un plugin dedicato è più stabile e più facile da implementare.