Vue d'ensemble
- Espace de noms
Websource\Newsletter, classes danssrc/(chargeur PSR-4 maison, pas de Composer). - Capacité requise pour l'administration et les routes REST protégées :
manage_options(filtrewebsource_newsletter_capability). - Fonctionne sans WooCommerce ; seul le code promo de bienvenue en dépend (
VoucherService::available()). - CSS (
wsx-newsletter) et JavaScript (wsx-newsletter, différé, sans jQuery) chargés seulement quand un formulaire est rendu.
| Classe | Rôle |
|---|---|
Plugin | Amorçage, conteneur de services, rendu du formulaire, code court, bloc, section de paquet de thème. |
Settings | Option unique, assainissement, textes par défaut, version du consentement, clé secrète. |
Schema, Repository | Tables, requêtes préparées, statistiques, limitation de débit. |
SubscriptionService | Inscription, confirmation, désinscription, effacement, renvoi, import, synchronisation avec le compte. |
MailService, VoucherService | E-mails (wp_mail, HTML + texte), code promo WooCommerce. |
Privacy | Exportateur, effaceur et texte de politique pour les outils de confidentialité de WordPress. |
Front\Endpoints, Front\Placements, Front\AccountSync | Routes REST et pages publiques, emplacements automatiques, case des comptes. |
Admin\AdminPage, Admin\SubscribersTable | Écran Newsletter (abonnés, réglages, import / export). |
Domain\* | AntiSpam, TokenService, EmailValidator, ConsentRecord, CsvReader, CsvWriter, Status (sans dépendance à WordPress). |
Données
| Élément | Contenu |
|---|---|
Table {prefix}wsx_newsletter_subscribers | id, user_id, email (unique), status (pending, confirmed, unsubscribed), source, locale, token_hash (SHA-256 du jeton de confirmation), token_expires, date_mail, consent_date, consent_text, consent_version, consent_locale, voucher_code, date_add, date_upd, date_confirm, date_unsub. Dates en UTC. |
Table {prefix}wsx_newsletter_throttle | ip_hash (HMAC-SHA-256 de l'adresse IP avec la clé secrète), date_add. Purgée après 24 h par l'évènement quotidien wsx_newsletter_daily. |
Option wsx_newsletter_settings | Tous les réglages (voir « Réglages »). Chargée automatiquement. |
Option wsx_newsletter_consent_version | Version courante du texte de consentement (entier ≥ 1). |
Option wsx_newsletter_secret | Clé secrète (64 caractères hexadécimaux) : signature du tampon anti-spam, jeton de désinscription, empreinte IP. |
Option wsx_newsletter_db_version | Version du schéma. |
Méta utilisateur wsx_newsletter_optin | 1 ou 0, miroir de l'abonnement du compte. |
Cookie wsnl_popup | Posé par le script quand la fenêtre a été ouverte ou après une inscription réussie. |
Aucune adresse IP n'est enregistrée en clair. Le jeton de confirmation n'est conservé que sous forme d'empreinte et est effacé à la confirmation.
Réglages
| Clé | Type | Défaut | Limites |
|---|---|---|---|
double_optin | booléen | true | |
token_hours | entier | 48 | 1 à 720 |
min_delay | entier | 3 | 0 à 60 secondes |
rate_max | entier | 5 | 1 à 100 par heure |
placements | liste | ["before_footer"] | before_footer, footer, home |
custom_hook | chaîne | "" | nom d'action : ^[a-zA-Z_][a-zA-Z0-9_./-]{2,63}$ |
signup_field | booléen | true | |
voucher_enabled | booléen | false | |
voucher_type | chaîne | percent | percent, amount |
voucher_value | décimal | 0 | 100 au plus en pourcentage |
voucher_days | entier | 30 | 1 à 365 |
voucher_min | décimal | 0 | |
popup_enabled | booléen | false | |
popup_mode | chaîne | delay | delay, exit |
popup_delay | entier | 10 | 3 à 300 secondes |
popup_days | entier | 7 | 1 à 365 |
title, intro, popup_title, popup_intro | chaîne | "" | 250 caractères, sans balise |
consent | chaîne | "" | 1 000 caractères, sans balise |
policy_url | chaîne | privacy | privacy, post:ID, /chemin, http(s)://… |
keep_data | booléen | true |
Un texte vide prend la valeur par défaut traduite. {shop} est remplacé par le nom du site.
Bloc Gutenberg websource/newsletter
Rendu serveur (block.json, apiVersion 3, éditeur sans build). Catégorie Widgets. Alignements large et pleine largeur.
| Attribut | Type | Défaut | Rôle |
|---|---|---|---|
layout | chaîne | card | card (carte) ou inline (bandeau sur une ligne). |
showIntro | booléen | true | Affiche le texte d'introduction. |
align | chaîne | wide ou full. | |
className | chaîne | Classes CSS supplémentaires. |
<!-- wp:websource/newsletter {"layout":"inline","showIntro":false} /-->
Code court [websource_newsletter]
| Attribut | Défaut | Rôle |
|---|---|---|
layout | card | card ou inline. |
intro | 1 | 0, no, false ou off masque l'introduction. |
class | Classes CSS supplémentaires. |
[websource_newsletter layout="inline" intro="0"]
Fonctions publiques
// Renvoie le HTML du formulaire. Options : layout (card|inline), intro (bool), class, source.
echo websource_newsletter_form(['layout' => 'inline']);
// Inscrit une adresse pour un visiteur qui a donné un consentement explicite (formulaire maison).
// Renvoie 'accepted', 'invalid' ou 'consent'. La double confirmation s'applique si elle est activée.
$result = websource_newsletter_subscribe('prenom.nom@example.test', true, 'api');
websource_newsletter_subscribe() enregistre le texte de consentement courant du site : ne l'appelez que si ce texte a réellement été présenté et accepté.
Emplacements automatiques
| Emplacement | Thème à blocs | Thème classique |
|---|---|---|
before_footer (bandeau) | Avant la partie de modèle du pied de page (render_block sur core/template-part). | Action get_footer. |
footer (carte) | Avant </footer> de la partie de modèle. | Action wp_footer (priorité 1), donc après le pied de page du thème. |
home (carte) | Avant </main> sur la page d'accueil. | Action loop_end de la requête principale, sur la page d'accueil. |
| Action libre | Action nommée dans custom_hook. | Idem. |
| Fenêtre | wp_footer (priorité 20), hors panier et commande. | Idem. |
Un thème peut placer lui-même le formulaire en déclenchant do_action('websource_before_footer') ou do_action('websource_footer'). Chaque emplacement n'est rendu qu'une fois par page.
Gabarits surchargeables
Copiez un gabarit de templates/ dans <thème>/websource-newsletter/ : form.php, fields.php, popup.php, result.php. Les variables sont dans $wsnl (ou $wsnl_result pour result.php).
Pages publiques
| Adresse | Rôle |
|---|---|
POST /?wsx_newsletter=subscribe | Inscription sans JavaScript. Champs : wsx_email, wsx_consent, wsx_stamp, wsx_source, website (champ piège). Affiche une page de résultat. |
GET /?wsx_newsletter=confirm&id=<id>&token=<jeton> | Confirme l'inscription. Réponses : confirmée, déjà confirmée, lien expiré, lien non valide. |
GET /?wsx_newsletter=unsubscribe&id=<id>&token=<jeton> | Affiche la page avec le bouton Me désinscrire (ne désinscrit pas). |
POST sur la même adresse avec wsx_unsubscribe=1 ou List-Unsubscribe=One-Click | Désinscrit (RFC 8058). |
Ces pages envoient X-Robots-Tag: noindex, nofollow, Referrer-Policy: no-referrer et les en-têtes anti-cache.
Routes REST
Espace de noms websource-newsletter/v1.
POST /subscribe
Publique par conception (formulaire ouvert à tous les visiteurs), protégée par le champ piège, le tampon signé, le délai minimal et la limitation de débit.
| Paramètre | Type | Rôle |
|---|---|---|
email | chaîne | Adresse à inscrire. |
consent | booléen | Case cochée. |
stamp | chaîne | Valeur du champ caché wsx_stamp du formulaire. |
website | chaîne | Champ piège, doit rester vide. |
source | chaîne | site, popup, home, footer, before_footer, block, shortcode, hook, widget. |
curl -X POST https://example.test/wp-json/websource-newsletter/v1/subscribe \
-H 'Content-Type: application/json' \
-d '{"email":"prenom.nom@example.test","consent":true,"stamp":"1791572911.fc19…","website":"","source":"site"}'
{"success":true,"message":"Merci ! Un e-mail vient de vous être envoyé : cliquez sur le lien pour confirmer votre inscription.","field":""}
La réponse est toujours en HTTP 200 avec Cache-Control: no-store. En cas d'erreur, success vaut false et field vaut email, consent ou une chaîne vide. La réponse est identique que l'adresse soit déjà connue ou non.
GET /stats
Capacité requise. Sans authentification : 401.
{"status":{"pending":2,"confirmed":3,"unsubscribed":1},"vouchers":0,"total":6}
GET /subscribers
Capacité requise.
| Paramètre | Type | Défaut | Rôle |
|---|---|---|---|
status | chaîne | "" | pending, confirmed, unsubscribed ou vide. |
search | chaîne | "" | Partie d'adresse. |
page | entier | 1 | |
per_page | entier | 25 | 1 à 100. |
curl -H 'X-WP-Nonce: <nonce>' --cookie '<cookies de session>' \
'https://example.test/wp-json/websource-newsletter/v1/subscribers?status=confirmed&per_page=1'
[{"id":7,"user_id":0,"email":"prenom.nom@example.test","status":"confirmed","source":"before_footer","locale":"fr_FR","date_mail":"2026-10-09 19:19:41","consent_date":"2026-10-09 19:19:41","consent_text":"J’accepte de recevoir la newsletter de …","consent_version":1,"consent_locale":"fr_FR","voucher_code":null,"date_add":"2026-10-09 19:19:41","date_upd":"2026-10-09 19:19:41","date_confirm":"2026-10-09 19:19:41","date_unsub":null}]
En-têtes X-WP-Total et X-WP-TotalPages. token_hash et token_expires ne sont jamais renvoyés.
DELETE /subscribers/<id>
Capacité requise. Efface l'abonné et sa preuve de consentement.
{"deleted":true}
Abonné inconnu : 404, code wsx_newsletter_not_found.
Filtres
| Filtre | Paramètres | Rôle |
|---|---|---|
websource_newsletter_capability | $capability | Capacité exigée (défaut manage_options). |
websource_newsletter_form_view | $view, $options | Données transmises au gabarit du formulaire. |
websource_newsletter_template | $file, $name | Fichier de gabarit utilisé (form, fields, popup, result). |
websource_newsletter_show_placements | $show | false masque les emplacements automatiques et la fenêtre sur la page courante. |
websource_newsletter_spam_verdict | $verdict, $email | Verdict anti-spam (ok, honeypot, too_fast, bad_stamp, rate_limited) : point d'accroche pour un captcha. |
websource_newsletter_client_ip | $ip | Adresse du visiteur pour la limitation de débit (derrière un proxy, renvoyer l'adresse transmise). |
websource_newsletter_mail | $mail, $type, $subscriber | E-mail avant envoi : subject, html, text, headers ; $type vaut confirm ou welcome. |
websource_newsletter_voucher_prefix | $prefix, $subscriber | Préfixe du code promo (défaut WELCOME, 12 caractères au plus). |
websource_theme_export | $sections | Ajoute la section newsletter à un paquet de thème. |
// Derrière Cloudflare : limiter le débit sur l'adresse réelle du visiteur.
add_filter('websource_newsletter_client_ip', static function ($ip) {
return isset($_SERVER['HTTP_CF_CONNECTING_IP']) ? sanitize_text_field(wp_unslash($_SERVER['HTTP_CF_CONNECTING_IP'])) : $ip;
});
Actions
| Action | Paramètres | Moment |
|---|---|---|
websource_newsletter_subscribed | $row | Demande d'inscription enregistrée (pas encore confirmée si la double confirmation est active). |
websource_newsletter_confirmed | $row, $voucher | Inscription confirmée ; $voucher vaut null ou ['code', 'text', 'until']. |
websource_newsletter_unsubscribed | $row | Désinscription (ligne avant modification). |
websource_newsletter_erased | $email | Abonné effacé. |
websource_newsletter_before_voucher_save | $coupon, $subscriber | Dernier ajustement du WC_Coupon avant enregistrement. |
websource_theme_import | $sections | Applique la section newsletter d'un paquet de thème. |
websource_before_footer, websource_footer | À déclencher par un thème pour accueillir le formulaire. |
// Envoyer chaque abonné confirmé à un outil d'envoi.
add_action('websource_newsletter_confirmed', static function (array $row): void {
mon_outil_ajouter_contact($row['email'], $row['locale']);
});
Outils de confidentialité
- Exportateur
websource-newsletter(wp_privacy_personal_data_exporters) : groupe « Abonnement à la newsletter » avec l'adresse, le statut, l'origine, la langue, la date, le texte et la version du consentement, les dates d'inscription, de confirmation et de désinscription, le code promo. - Effaceur
websource-newsletter(wp_privacy_personal_data_erasers) : supprime la ligne de l'abonné, remet la méta du compte à0, supprime le code promo de bienvenue non utilisé (ou retire la restriction d'adresse d'un code déjà utilisé). - Texte proposé dans Réglages > Confidentialité > Guide de politique.
Export CSV et JSON
CSV (séparateur ;, UTF-8 avec BOM, cellules commençant par =, +, -, @ neutralisées) :
email;status;source;lang;consent_date;consent_version;consent_text;date_add;date_confirm;date_unsub;voucher_code
JSON des réglages d'affichage (section newsletter, version 1). Une page est désignée par @type:slug, jamais par identifiant.
{
"plugin": "websource-newsletter",
"section": "newsletter",
"version": 1,
"data": {
"placements": ["before_footer"],
"custom_hook": "",
"signup_field": true,
"popup_enabled": false,
"popup_mode": "delay",
"popup_delay": 10,
"popup_days": 7,
"title": "",
"intro": "",
"consent": "",
"policy_url": "privacy",
"popup_title": "",
"popup_intro": ""
}
}
Jamais exportés ni importés : abonnés, clé secrète, version du consentement, code promo, anti-spam. Un texte de consentement différent à l'import augmente la version.
Variables CSS
Le formulaire lit les variables du thème avec une valeur de repli. Surchargez les variables --wsnl-* sur .wsnl.
| Variable | Lue depuis | Repli |
|---|---|---|
--wsnl-bg | --wst-surface, --color-surface | #fff |
--wsnl-ink | --wst-ink, --color-ink | #14282b |
--wsnl-line | --wst-line, --color-line | #d9e6e4 |
--wsnl-accent | --wst-brand-fill, --color-brand-fill, --color-brand-600 | #0f766e |
--wsnl-accent-ink | --wst-brand-on, --color-brand-on | #fff |
--wsnl-link | --wst-brand-text, --color-brand-text | #0b5f58 |
--wsnl-radius | --wst-radius-2xl, --radius-2xl | 1.5rem |
--wsnl-field-radius | --wst-radius-xl, --radius-xl | 1rem |
--wsnl-shadow | --wst-card-shadow | none |
Classes : .wsnl, .wsnl--card, .wsnl--inline, .wsnl--placed, .wsnl--at-before-footer, .wsnl--at-footer, .wsnl--at-home, .wsnl--at-custom, .wsnl--popup, .wsnl-popup (<dialog>). Le bandeau passe en deux colonnes quand le formulaire lui-même dépasse 52 rem (requête de conteneur).
Polylang et WPML
Les textes saisis (title, intro, consent, popup_title, popup_intro) sont déclarés par pll_register_string (groupe « Websource Newsletter ») et wpml-config.xml. L'e-mail est envoyé dans la langue enregistrée avec l'abonné quand elle est installée sur le site.
Désinstallation
uninstall.php supprime l'évènement planifié. Si keep_data est décoché, il supprime aussi les deux tables, les quatre options et les métas utilisateur wsx_newsletter_optin et wsx_newsletter_per_page.