Vue d'ensemble
- Espace de noms PHP :
Websource\LinkLists(classes danssrc/, PSR-4, chargeur danswebsource-linklists.php, pas de Composer). - Préfixe des options, filtres et handles :
wsx_linklists_. Classes CSS :wsll. - Stockage : options WordPress, aucune table.
wsx_linklists_lists(listes publiées, chargée automatiquement),wsx_linklists_drafts(brouillons),wsx_linklists_seq(dernier numéro),wsx_linklists_rev(révision du cache),wsx_linklists_version. - Cache : un transient par liste et par langue,
wsx_linklists_v_<révision>_<numéro>-<langue>, durée 1 heure. Vidé à chaque enregistrement et quand une page, un produit, un terme, les permaliens, le thème ou les pages WooCommerce changent. - Côté public : une feuille de style (
assets/css/front.css, handlewsx-linklists), aucun JavaScript. La feuille n'est chargée que si une liste s'affiche sur la page. - Droit requis pour l'administration :
edit_theme_options(filtrewsx_linklists_capability). - Aucune route REST propre. L'administration passe par une action
admin-ajax.phpprotégée (voir « Action AJAX d'administration »).
Bloc
websource/link-list — rendu serveur, sans étape de build (blocks/link-list/block.json, blocks/link-list/editor.js).
| Attribut | Type | Défaut | Rôle |
|---|---|---|---|
list | string | "" | Identifiant de la liste (informations) ou son numéro. |
layout | string | "" | Présentation forcée pour ce bloc : columns, inline ou menu. Vide : celle de la liste. |
Supports : align (wide, full), spacing.margin, spacing.padding, customClassName, plusieurs blocs par page.
<!-- wp:websource/link-list {"list":"informations","layout":"menu"} /-->
Le bloc ne rend rien sur le site si la liste est inconnue, masquée ou sans lien affichable. Dans l'éditeur, il affiche alors un message d'aide.
Code court
[websource_linklist list="informations"]
[websource_linklist id="1"]
| Attribut | Rôle |
|---|---|
list | Identifiant lisible de la liste. Stable après un export puis un import. |
id | Numéro de la liste. Change si la liste est recréée par un import en remplacement. |
Widget
Widget classique wsx_linklists (classe Websource\LinkLists\Widget\LinkListWidget, classe CSS widget_wsx_linklists). Réglages d'instance : title (facultatif) et list (identifiant). Disponible aussi comme widget hérité dans l'éditeur de widgets à blocs.
Fonction PHP
<?php
// Dans un fichier du thème : HTML d'une liste affichée sur le site, ou chaîne vide.
echo websource_linklist('informations'); // identifiant
echo websource_linklist(1); // numéro
Accès aux services :
<?php
$plugin = \Websource\LinkLists\Plugin::instance();
$html = $plugin->renderer()->renderByRef('informations'); // comme websource_linklist()
$html = $plugin->renderer()->renderList(1, 2); // numéro, colonne du pied de page (0 à 4)
$all = $plugin->lists()->all(); // listes publiées, par numéro
$active = $plugin->lists()->findActive();
$plugin->afterChange(); // vide le cache après une écriture directe
Emplacements
Un emplacement est une paire page + endroit. Son champ hook prend l'une de ces trois formes :
| Forme | Exemple | Rendu |
|---|---|---|
zone:<zone> | zone:footer | Zone gérée par l'extension. |
menu:<emplacement> | menu:primary | Éléments <li class="menu-item wsll-menu-item"> ajoutés à la fin de cet emplacement de menu (filtre wp_nav_menu_items, thèmes classiques). |
| nom d'action | woocommerce_after_single_product_summary | Liste affichée sur cette action WordPress ou WooCommerce, dans <div class="wsll-zone wsll-zone--hook">. Sur loop_start et loop_end, seule la boucle principale est concernée. |
Zones et points d'accroche :
Position (position) | Zone | Thème à blocs | Thème classique | Storefront |
|---|---|---|---|---|
topbar_left, topbar_right | topbar | wp_body_open (5) | wp_body_open (5) | wp_body_open (5) |
under_header | under_header | après la partie de modèle header | après le premier </header> placé avant <main> ou <article> (tampon de sortie ouvert sur wp_body_open, fermé sur get_footer) ; à défaut, en haut de page | storefront_before_content (5) |
before_footer | before_footer | avant la partie de modèle footer | get_footer (5) | storefront_before_footer (10) |
footer_col1 à footer_col4 | footer | à l'intérieur de <footer>, en premier | get_footer (10) | storefront_footer (5) |
after_footer | after_footer | après la partie de modèle footer | wp_footer (5) | storefront_after_footer (10) |
left_column | sidebar_top | — | dynamic_sidebar_before | dynamic_sidebar_before |
right_column | sidebar_bottom | — | dynamic_sidebar_after | dynamic_sidebar_after |
account_block | account | dans le <nav> du compte WooCommerce, après son menu | idem | idem |
Les zones de widgets dont l'identifiant contient footer, header, colophon ou bottom ne reçoivent pas les positions left_column et right_column (filtre wsx_linklists_sidebar_zone).
Clés de page (page) : all, home, shop, category, product, brand, cms (avec cms_id, 0 = toutes les pages), blog, search, login, password, account, cart, checkout, contact, notfound.
Les points d'accroche ne sont branchés que sur les pages où une liste affichée les utilise (Websource\LinkLists\Front\Placements::prepare(), action wp, priorité 20). Rien n'est branché en administration, en AJAX, en REST, dans les flux ni dans les contenus embarqués.
Action AJAX d'administration
L'extension ne déclare aucune route REST. L'écran Apparence > Listes de liens appelle une seule action.
- Méthode et chemin :
POST /wp-admin/admin-ajax.php - Paramètres communs :
action=wsx_linklists,_ajax_nonce(noncewsx_linklists_admin),do(opération). - Accès : utilisateur connecté disposant du droit
edit_theme_options. Un visiteur anonyme reçoit la réponse0de WordPress (HTTP 400), un utilisateur sans le droit ou sans nonce valide reçoit HTTP 403.
do | Paramètres | Réponse |
|---|---|---|
create | name, starter (blank ou footer) | { "id": 6, "url": "…&edit=6" } |
draft | id, payload (JSON) | { "preview": "<nav…>", "saved_at": "10:42" } — le site n'est pas modifié |
save | id, payload (JSON) | { "ok": true, "preview": "…", "saved_at": "10:42", "content": {…}, "dropped": 0 } |
toggle | id, active (0 ou 1) | { "ok": true } |
duplicate | id | { "id": 7, "url": "…&edit=7" } |
delete | id | { "ok": true } |
search | q (nom ou UGS) | { "results": [ { "id": 16, "name": "T-shirt Lagon" } ] } (15 au plus) |
Exemple de requête :
curl -b cookies.txt https://exemple.fr/wp-admin/admin-ajax.php \
-d action=wsx_linklists -d do=toggle -d id=1 -d active=0 -d _ajax_nonce=20c18e7621
Réponses d'erreur (JSON { "error": "…" }) : 400 action inconnue, 403 droit ou nonce refusé, 404 liste introuvable.
Le contenu reçu (payload) est toujours nettoyé par Websource\LinkLists\Admin\ListPayloadSanitizer : 60 liens et 20 emplacements au plus, sources et pages connues seulement, adresses passées par UrlSanitizer. dropped donne le nombre de liens refusés.
Format des données
Liste publiée (option wsx_linklists_lists)
{
"id": 1,
"identifier": "informations",
"name": "Informations",
"layout": "columns",
"show_title": true,
"active": true,
"titles": { "fr": "Informations" },
"links": [
{ "source": "page", "target_id": 0, "page_key": "contact", "url": "", "urls": [], "new_tab": false, "nofollow": false, "labels": [] },
{ "source": "custom", "target_id": 0, "page_key": "", "url": "https://www.websource.fr/", "urls": [], "new_tab": true, "nofollow": true, "labels": { "fr": "Websource" } }
],
"placements": [
{ "page": "all", "position": "footer_col1", "cms_id": 0, "hook": "zone:footer" }
],
"date_add": "2026-10-09 18:52:10",
"date_upd": "2026-10-09 18:52:10"
}
| Champ d'un lien | Rôle |
|---|---|
source | custom (lien libre), cms (page WordPress), category (product_cat), brand (product_brand), product, page (page du site). |
target_id | Identifiant de la page, du terme ou du produit pour cms, category, brand, product. |
page_key | Pour page : home, shop, blog, contact, sitemap, terms, privacy, stores, best-sales, new-products, prices-drop, manufacturers, authentication, my-account, history, cart. |
url, urls | Lien libre : adresse commune et adresses par code de langue. |
labels | Texte affiché par code de langue. Vide : nom de la cible. Obligatoire pour un lien libre. |
new_tab, nofollow | target="_blank" + rel="noopener", rel="nofollow". |
Adresses acceptées pour un lien libre (Websource\LinkLists\Link\UrlSanitizer) : http://, https://, chemin commençant par /, ancre #…, mailto:, tel:. Une adresse du type www.exemple.fr ou exemple.fr/page est complétée par https://. Tout le reste est refusé (javascript:, ftp:, espaces, guillemets, chevrons). 500 caractères au plus.
Un lien dont la cible n'existe plus ou n'est pas publiée reste enregistré mais n'est pas affiché.
Pages du site sans équivalent natif : contact, sitemap, terms, stores, prices-drop et manufacturers sont cherchées parmi les pages publiées, par slug (contact, plan-du-site, conditions-generales-de-vente, nos-magasins, promotions, marques et leurs variantes ; liste dans Websource\LinkLists\Link\SpecialPages::SLUGS). terms utilise d'abord la page des conditions générales de WooCommerce, privacy la page de politique de confidentialité de WordPress. best-sales et new-products mènent à la boutique triée (?orderby=popularity, ?orderby=date).
Vue d'une liste (filtre wsx_linklists_view, gabarit)
<?php
[
'id' => 1,
'identifier' => 'informations',
'title' => 'Informations',
'layout' => 'columns', // columns, inline, menu
'show_title' => true,
'links' => [
['url' => 'https://exemple.fr/contact/', 'label' => 'Contact', 'target' => '', 'rel' => ''],
],
];
Paquet de thème
Section linklists, version 1, des filtres websource_theme_export et websource_theme_import de Websource Theme Config (classe Websource\LinkLists\ThemePackage\ThemePackage).
{
"lists": [
{
"identifier": "informations",
"name": "Informations",
"layout": "columns",
"show_title": true,
"active": true,
"titles": { "fr": "Informations" },
"links": [
{ "source": "cms", "target": "cms:a-propos", "page_key": "", "url": "", "urls": [], "new_tab": false, "nofollow": false, "labels": [] },
{ "source": "product", "target": "product:WS-001", "page_key": "", "url": "", "urls": [], "new_tab": false, "nofollow": false, "labels": [] }
],
"placements": [
{ "page": "cms", "position": "before_footer", "hook": "zone:before_footer", "cms": "cms:a-propos" }
]
}
]
}
- Clés stables des cibles :
cms:<slug>,category:<slug>,brand:<slug>,product:<UGS>(ouproduct:<slug>sans UGS). - Contexte d'import :
mode(replacepar défaut, oumerge) etdry_run.replacesupprime les listes du site puis crée celles du paquet (les numéros changent, les identifiants sont conservés).mergemet à jour la liste de même identifiant et ajoute les autres. - Rapport renvoyé :
{ "action": "merge", "counts": { "create": 1, "update": 1, "delete": 0, "skip": 0 }, "messages": ["linklists : cible « cms:nexiste-pas » introuvable : lien ignoré."] }. - Une section de version supérieure à 1 lève une
RuntimeException(rapportée en erreur par Websource Theme Config). - Les brouillons ne sont pas exportés. 60 listes au plus par import.
- Paquets du module PrestaShop : les pages
index,manufacturer,pagenotfound,my-account,authentication,orderetproduct-listsont traduites enhome,brand,notfound,account,login,checkoutetcategory. Un hook libredisplayXxxdevient l'actionwsx_linklists_displayxxx, à déclencher dans le thème avecdo_action(). Sur un site à une seule langue, le premier texte du paquet est repris.
Filtres
| Filtre | Paramètres | Rôle |
|---|---|---|
wsx_linklists_view | array $view, int $order | Modifier une liste avant affichage (présentation, titre, liens). |
wsx_linklists_template | string $path, array $view | Chemin du gabarit utilisé. |
wsx_linklists_list_html | string $html, array $view, int $order | HTML final d'une liste (données structurées comprises). |
wsx_linklists_zone_hooks | array $hooks, string $template | Déclarer les actions du thème pour les zones under_header, before_footer, footer, after_footer : [zone => [action, priorité]]. |
wsx_linklists_account_hook | string $hook | Action de la zone du compte client (défaut woocommerce_after_account_navigation). |
wsx_linklists_sidebar_zone | bool $show, string $sidebar, string $zone | Autoriser ou non une zone de widgets pour les colonnes latérales. |
wsx_linklists_suggested_hooks | array $hooks | Suggestions du champ « Nom du hook » : [nom d'action => libellé]. |
wsx_linklists_current_page | string $page | Clé de la page affichée, pour un type de page propre au site. |
wsx_linklists_special_page_id | int $id, string $key | Identifiant de la page WordPress d'une « page du site ». |
wsx_linklists_special_page | array\|null $result, string $key, string $language | Fournir ou masquer une « page du site » : ['url' => …, 'label' => …] ou null. |
wsx_linklists_capability | string $capability | Droit requis pour gérer les listes. |
wsx_linklists_woocommerce_active | bool $active | Forcer la présence ou l'absence de la boutique. |
Exemple : déclarer les zones d'un thème classique.
<?php
add_filter('wsx_linklists_zone_hooks', function (array $hooks, string $template): array {
if ($template === 'mon-theme') {
$hooks['under_header'] = ['mon_theme_after_header', 10];
$hooks['footer'] = ['mon_theme_footer_top', 10];
}
return $hooks;
}, 10, 2);
Exemple : forcer la présentation d'une liste.
<?php
add_filter('wsx_linklists_view', function (array $view): array {
if ($view['identifier'] === 'informations') {
$view['layout'] = 'inline';
}
return $view;
});
Gabarit et balisage
Gabarit : templates/list.php, surchargeable dans le thème (<thème>/websource-linklists/list.php). Variables : $wsll_list (la vue) et $wsll_order (colonne du pied de page, 0 à 4).
<nav class="wsll wsll--columns wsll--o1" data-wsll-list="informations" aria-label="Informations">
<p class="wsll__title">Informations</p>
<ul class="wsll__list">
<li class="wsll__item"><a class="wsll__link" href="https://exemple.fr/contact/">Contact</a></li>
</ul>
</nav>
| Classe | Rôle |
|---|---|
wsll--columns, wsll--inline, wsll--menu | Présentation. |
wsll--o1 à wsll--o4 | Ordre dans la grille du pied de page (order). |
wsll-zone, wsll-zone--topbar, --under-header, --before-footer, --footer, --after-footer, --sidebar, --account, --hook | Conteneur d'une zone. |
wsll-zone__side, wsll-zone__side--end | Côtés de la barre du haut. |
wsll-block | Conteneur du bloc. |
wsll-menu-item | Élément ajouté à la fin d'un menu du thème. |
Données structurées : après chaque liste affichée, une balise <script type="application/ld+json"> de type SiteNavigationElement (@graph, nom et adresse absolue de chaque lien http(s)). Une même liste affichée deux fois sur une page n'est décrite qu'une fois. Rien dans l'aperçu de l'administration ni dans l'éditeur de blocs.
Variables CSS
La feuille de style lit les variables du thème Websource et prévoit une valeur de repli pour tout autre thème.
| Variable | Repli | Utilisation |
|---|---|---|
--wst-ink, puis --color-ink | #14282b | Texte et titre. |
--wst-brand, puis --color-brand-600 | #0f766e | Survol, contour au clavier. |
--wst-brand-soft, puis --color-brand-50 | #effaf8 | Fond au survol du menu vertical. |
--wst-line, puis --color-line | #d9e6e4 | Bordures des pastilles, du menu vertical, de la barre du haut. |
--wst-surface, puis --color-surface | #fff | Fond du menu vertical. |
--wst-muted, puis --color-muted | #5b6f72 | Message du bloc vide. |
--wst-radius, puis --radius-xl | 1rem | Arrondi du menu vertical. |
--wst-card-shadow | none | Ombre du menu vertical. |
--wp--style--global--wide-size, puis --wst-container | 1200px | Largeur maximale des zones. |
Multilingue
- Les titres, les textes des liens et les adresses des liens libres sont enregistrés par code de langue (
fr,en…). La langue courante est lue auprès de Polylang (pll_current_language) ou de WPML (wpml_current_language) ; à défaut, la langue du site. - Texte absent dans une langue : celui de la langue par défaut est repris.
- Les pages, produits et termes sont remplacés par leur traduction (
pll_get_post,pll_get_term,wpml_object_id). - Les titres et textes des listes publiées sont déclarés à Polylang (
pll_register_string, groupe « Websource Link Lists ») et à WPML (wpml-config.xml, optionwsx_linklists_lists). - Classe utilitaire :
Websource\LinkLists\Languages(all(),codes(),defaultCode(),current(),isMultilingual(),translatePost(),translateTerm(),pick()).
Classes PHP utiles
| Classe | Rôle |
|---|---|
Plugin | Services, code court, vidage du cache (afterChange()). |
Repository\ListRepository | Lecture et écriture des listes et des brouillons. |
Admin\ListPayloadSanitizer | Nettoyage d'un contenu de liste. |
Link\LinkSources, Link\LinkResolver, Link\SpecialPages, Link\UrlSanitizer | Sources de liens, résolution en adresse et texte, pages du site, adresses libres. |
Placement\PlacementCatalog, Placement\PageDetector, Placement\Dispatcher | Pages et positions, détection de la page affichée, listes à afficher sur un point d'accroche. |
Front\Placements | Branchement des zones et des actions. |
Render\Renderer, Render\ListViewBuilder, Render\ListViewProvider, Render\ListCache, Render\JsonLdBuilder | Rendu, vue, cache, données structurées. |
Block\LinkListBlock, Widget\LinkListWidget | Bloc et widget. |
ThemePackage\ThemePackage | Export et import du paquet de thème. |
Désinstallation
uninstall.php supprime les options wsx_linklists_lists, wsx_linklists_drafts, wsx_linklists_seq, wsx_linklists_rev, wsx_linklists_version, widget_wsx_linklists et les transients wsx_linklists_*, sur chaque site d'un réseau multisite.