Vue d'ensemble
- Espace de noms
Websource\StoreInfo, classes danssrc/(chargeur PSR-4 maison, pas de Composer). - Capacité requise pour l'administration et toutes les routes REST :
manage_options(filtrewebsource_storeinfo_capability). - Fonctionne sans WooCommerce. Les emplacements « fiche produit » et « panier » s'appuient sur ses hooks.
- CSS (
wsx-storeinfo) et JavaScript (wsx-storeinfo, différé, sans dépendance) chargés seulement quand un bloc est rendu. - Aucun appel externe côté serveur, sauf le téléchargement d'une photo lors d'un import (adresses publiques seulement,
download_url).
| Classe | Rôle |
|---|---|
Plugin | Amorçage, services, rendu, code court, bloc, JSON-LD, section de paquet de thème. |
Hours\* | Logique d'horaires sans dépendance : HoursParser, WeeklySchedule, Season, ClosureException, OpeningSchedule, OpeningCalculator, OpeningStatus, StatusPresenter, Labels, DayMath, TimeRange, ParsedDay. |
Store\StoreRepository, StoreSanitizer, StoreSelector, ScheduleFactory, StoreViewBuilder, ViewContext | Stockage, assainissement, choix des magasins, préparation de la vue. |
Settings\Settings, SettingsStore, PlacementCatalog, PageDetector | Réglages, emplacements, type de page. |
Render\StoreRenderer, DesignStyle, Contrast, EmailObfuscator, SchemaOrgBuilder | Rendu, variables CSS, contraste, e-mail encodé, JSON-LD. |
Directions\DirectionsBuilder | Liens d'itinéraire et adresse de la carte. |
Front\Dispatcher | Emplacements automatiques (thèmes à blocs et classiques). |
Rest\RestController, Admin\AdminPage, ThemePackage\ThemePackage | REST, écran Infos magasin, export / import. |
php tests/run.php exécute 48 vérifications de la logique d'horaires sans WordPress (plages multiples, minuit, jours fériés, fuseaux, changement d'heure, saisons).
Données
| Option | Contenu |
|---|---|
wsx_storeinfo_stores | Liste de magasins : id, slug, position, active, content. 50 au plus. |
wsx_storeinfo_next_id | Prochain numéro de magasin. |
wsx_storeinfo_settings | Réglages généraux. |
wsx_storeinfo_placements | Emplacements (30 au plus). |
Contenu d'un magasin
| Clé | Format |
|---|---|
name | Texte, 120 caractères, obligatoire. |
description | Texte multiligne, 1 500 caractères. |
note | Texte, 300 caractères. |
address1, address2, postcode, city, country | Textes. |
country_iso | Deux lettres majuscules. |
phone | Texte, 30 caractères. |
email | Adresse valide ou vide. |
latitude, longitude | Décimal en texte, 6 décimales, ou vide. |
image_id | Identifiant d'une image de la médiathèque, ou 0. |
hours | Objet 1 (lundi) à 7 (dimanche) : texte saisi, 80 caractères. |
seasons | 6 au plus : label, from et to (MM-JJ), hours. |
exceptions | 60 au plus : label, from et to (AAAA-MM-JJ), hours (vide = fermé). |
Réglages
| Clé | Type | Défaut |
|---|---|---|
main_store | entier | 0 (premier magasin actif) |
multi_display | main ou list | main |
schema | booléen | true |
map | booléen | true |
providers | liste parmi google, waze, apple, osm | les quatre |
show | objet de booléens : photo, description, phone, email, hours, directions | tous true |
follow_theme | booléen | true |
design | bg, title_color (#rrggbb ou vide), borders, radius (none, sm, md, lg), width (box, wide) | |
contact_page | identifiant de page, 0 = page /contact | 0 |
keep_data | booléen | true |
Emplacement
| Clé | Valeurs |
|---|---|
position | header_bar, under_header, footer_col, before_footer, contact_page, home, product_pickup, cart, custom |
page | all, home, shop, category, product, cms, blog, contact, cart, checkout, account (imposé pour contact_page, home, product_pickup, cart) |
hook | Nom d'action WordPress, pour custom seulement. Les actions d'en-tête et d'administration sont refusées. |
layout | compact, card, footer |
store | main, all ou un numéro de magasin |
title | Texte, 80 caractères |
Bloc Gutenberg websource/storeinfo
Rendu serveur (block.json, apiVersion 3, éditeur sans build). Catégorie Widgets. Alignements large et pleine largeur.
| Attribut | Type | Défaut | Rôle |
|---|---|---|---|
store | chaîne | main | main, all, numéro ou identifiant court du magasin. |
layout | chaîne | card | compact, card ou footer. |
title | chaîne | Titre du bloc. | |
align | chaîne | wide ou full. | |
className | chaîne | Classes CSS supplémentaires. |
<!-- wp:websource/storeinfo {"store":"main","layout":"card","title":"Notre magasin"} /-->
Code court [websource_storeinfo]
| Attribut | Défaut | Rôle |
|---|---|---|
store | main | main, all, numéro ou identifiant court. |
layout | card | compact, card ou footer. |
title | Titre du bloc. | |
class | Classes CSS supplémentaires. |
[websource_storeinfo store="magasin-de-demonstration" layout="compact"]
Un magasin inconnu ou masqué ne produit aucune sortie.
Fonctions publiques
// HTML du bloc. $store : 'main', 'all', numéro ou identifiant court. $layout : compact|card|footer.
echo websource_storeinfo_render('main', 'card', 'Notre magasin');
// État d'ouverture dans le fuseau du site, ou null si le magasin n'existe pas.
$status = websource_storeinfo_status('main');
// ['state' => 'closed', 'text' => 'Fermé – ouvre demain à 9h'] (state : open|closing|closed|unknown)
Emplacements automatiques
| Position | Thème à blocs | Thème classique |
|---|---|---|
header_bar | Avant la partie de modèle de l'en-tête. | Action wp_body_open. |
under_header | Après la partie de modèle de l'en-tête. | Actions connues de thèmes (filtre websource_storeinfo_position_hooks) ; sinon aucune sortie. |
footer_col | Avant </footer> de la partie de modèle. | Actions connues de thèmes ; sinon wp_footer, sous le pied de page. |
before_footer | Avant la partie de modèle du pied de page. | Action get_footer. |
contact_page | Filtre the_content de la page de contact. | Idem. |
home | Avant </main> sur l'accueil. | loop_end de la requête principale ou woocommerce_after_main_content. |
product_pickup | Après le bloc d'ajout au panier, ou woocommerce_single_product_summary (priorité 35). | woocommerce_single_product_summary (priorité 35). |
cart | Après le bloc woocommerce/cart. | woocommerce_after_cart. |
custom | Action nommée dans hook. | Idem. |
Un thème peut accueillir le bloc avec do_action('websource_after_header'), do_action('websource_before_footer') ou do_action('websource_footer'). Chaque position est rendue une fois par page.
Gabarit surchargeable
Copiez templates/store.php dans <thème>/websource-storeinfo/store.php. Les variables sont dans $wssi : stores (magasins prêts à afficher), layout, title, style, wide, multi, show, class.
Routes REST
Espace de noms websource-storeinfo/v1. Toutes les routes exigent la capacité de gestion ; sans authentification : 401.
| Méthode | Chemin | Paramètres | Rôle |
|---|---|---|---|
| GET | /stores | Liste des magasins. | |
| POST | /stores | id (0 = création), active, content | Crée ou met à jour un magasin. 422 sans nom. |
| DELETE | /stores/<id> | Supprime un magasin. | |
| POST | /stores/<id>/active | active | Affiche ou masque. |
| POST | /stores/order | ids (liste d'entiers) | Nouvel ordre. |
| GET | /settings | Réglages et emplacements. | |
| POST | /settings | settings, placements | Enregistre (valeurs assainies renvoyées). |
| POST | /preview | store, settings, layout, title, at (AAAA-MM-JJTHH:MM), check | Aperçu HTML et explication des horaires. |
| GET | /export | contact (booléen) | Fichier d'export. |
| POST | /import | package, mode (replace, merge), dry_run | Analyse ou import. |
curl -X POST https://example.test/wp-json/websource-storeinfo/v1/stores \
-H 'X-WP-Nonce: <nonce>' --cookie '<cookies de session>' -H 'Content-Type: application/json' \
-d '{"id":0,"active":true,"content":{"name":"Magasin de démonstration","city":"Ville-Exemple","hours":{"1":"9h-12h / 14h-18h"}}}'
{"store":{"id":1,"slug":"magasin-de-demonstration","active":true,"content":{"name":"Magasin de démonstration","description":"","note":"","address1":"","address2":"","postcode":"","city":"Ville-Exemple","country":"","country_iso":"","phone":"","email":"","latitude":"","longitude":"","image_id":0,"hours":{"1":"9h-12h / 14h-18h","2":"","3":"","4":"","5":"","6":"","7":""},"seasons":[],"exceptions":[]},"image_url":""}}
Réponse de /import :
{"action":"merge","counts":{"create":2,"update":1,"delete":0,"skip":2},"messages":["Un magasin sans nom a été ignoré."],"dry_run":true,"stores":[],"settings":{},"placements":[]}
Filtres
| Filtre | Paramètres | Rôle |
|---|---|---|
websource_storeinfo_capability | $capability | Capacité exigée (défaut manage_options). |
websource_storeinfo_view | $view | Données transmises au gabarit. |
websource_storeinfo_template | $file, $name | Fichier de gabarit utilisé. |
websource_storeinfo_current_page | $page | Type de la page courante. |
websource_storeinfo_show_placements | $show | false masque les emplacements automatiques sur la page courante. |
websource_storeinfo_position_hooks | $actions, $position | Actions de thèmes classiques pour under_header et footer_col. |
websource_storeinfo_labels | $labels, $iso | Textes du badge (clés de Hours\Labels::FR) pour une autre langue. |
websource_storeinfo_organization_id | $id | @id du nœud Organization auquel rattacher les magasins (parentOrganization). Vide par défaut. |
websource_theme_export | $sections | Ajoute la section storeinfo à un paquet de thème (sans coordonnées). |
// Rattacher les magasins au nœud Organization émis par le thème.
add_filter('websource_storeinfo_organization_id', static function () {
return home_url('/#organization');
});
Actions
| Action | Rôle |
|---|---|
websource_theme_import ($sections) | Applique la section storeinfo d'un paquet de thème (mode remplacer). |
websource_after_header, websource_before_footer, websource_footer | À déclencher par un thème pour accueillir le bloc. |
Données structurées
Un nœud LocalBusiness par magasin actif, dans le <head> : @id{accueil}#store-<identifiant court>, name, description, url, image, telephone, email, address, geo et hasMap (si la latitude et la longitude sont valides), openingHoursSpecification (semaine, saisons avec validFrom / validThrough, exceptions). Le magasin principal est émis sur toutes les pages, tous les magasins actifs sur la page de contact. Le réglage schema les désactive.
Export JSON
{
"plugin": "websource-storeinfo",
"section": "storeinfo",
"version": 1,
"data": {
"contact_included": false,
"settings": {"main_store": "magasin-de-demonstration", "contact_page": "@page:contact"},
"placements": [{"position": "footer_col", "page": "all", "hook": "", "layout": "footer", "store": "slug:magasin-de-demonstration", "title": "Notre magasin"}],
"stores": [{"slug": "magasin-de-demonstration", "active": true, "content": {"name": "Magasin de démonstration", "image": "https://example.test/wp-content/uploads/photo.jpg", "hours": {"1": "9h-12h"}}}]
}
}
- Les magasins sont désignés par leur identifiant court, la page de contact par
@page:<slug>, la photo par son adresse. - Sans
contact_included, les champsaddress1,address2,postcode,city,country,country_iso,phone,email,latitude,longitudesont vides ; à l'import, ceux d'un magasin existant de même identifiant sont conservés. keep_datan'est jamais exporté ni importé.- À l'import, une photo de ce site est réutilisée ; une autre adresse est téléchargée dans la médiathèque (JPEG, PNG ou WebP, 8 Mo).
- Les paquets du module PrestaShop sont acceptés : textes par code de langue (celui de la langue du site est gardé), emplacements
display*inconnus ignorés avec un message.
Variables CSS
Le bloc lit les variables du thème avec une valeur de repli. Surchargez les variables --wssi-* sur .wssi.
| Variable | Lue depuis | Repli |
|---|---|---|
--wssi-bg | --wst-surface, --color-surface | #fff |
--wssi-fg | --wst-ink, --color-ink | #14282b |
--wssi-line | --wst-line, --color-line | #d9e6e4 |
--wssi-title | couleur du texte | |
--wssi-radius | --wst-radius, --radius-2xl | 1.5rem |
--wssi-shadow | --wst-card-shadow | none |
Couleur d'accent : --wst-brand, puis --color-brand-600 (repli #0f766e). Colonne de pied de page : --color-footer-ink, --color-footer-muted, --color-footer-line.
Classes : .wssi, .wssi--compact, .wssi--card, .wssi--footer, .wssi--wide, .wssi--multi, .wssi--placed, .wssi--at-<position>, .wssi__badge.is-open|is-closing|is-closed|is-unknown. Dans la carte, la photo passe à côté du texte quand la carte dépasse 46 rem (requête de conteneur).
Polylang et WPML
Les textes saisis (nom, description, note, pays, libellés de saisons et de fermetures, titres d'emplacements) sont déclarés par pll_register_string (groupe « Websource Store Info ») et wpml_register_single_string. Les textes du badge existent en français et en anglais (filtre websource_storeinfo_labels pour une autre langue).
Désinstallation
uninstall.php supprime les quatre options si keep_data est décoché. Les photos restent dans la médiathèque.