Espace tutos dev’ & informatique 🐧

WordPress et le protocole OpenSearch

Publié le
Article sur comment ajouter facilement le support de OpenSearch sur votre site WordPress

Introduction

La recherche sur un site web est un sujet épineux. Est-ce que les utilisateurs s’en servent vraiment ? A partir de quelle quantité de contenu faut-il l’envisager ? Faut-il gérer les fautes d’orthographe ?

Le CMS WordPress inclut de base une fonction de recherche. Dans l’article d’aujourd’hui, on va voir comment permettre à un visiteur d’installer le moteur de recherche d’un site WordPress dans son navigateur, afin qu’il puisse y faire ses requêtes sans avoir à l’ouvrir au préalable. Une petite touche de finishing bien sympathique !

Bonus, on travaillera sans plugin (la légèreté c’est bien) et le code sera facile à copier/coller dans tous vos projets 😉

Avant de commencer à coder, je vais d’abord décrire un peu le protocole OpenSearch que l’on va utiliser.
Si vous êtes pressé ou si vous voulez juste le code PHP à insérer dans votre site WordPress, rendez-vous directement dans la section contenant le code complet.

Note : le code a été testé sur une installation WordPress 5.8 et PHP 7.4. N’oubliez pas que les modifications du code d’un site WordPress doivent toujours s’effectuer soit dans un thème enfant soit dans un thème / plugin maison, afin de ne rien perdre lors des mises à jour !

OpenSearch, un standard de la recherche sur le web

Détection et ajout des moteurs de recherche par les navigateurs

Si vous avez déjà exploré les réglages de votre navigateur web, vous avez sans doute déjà rencontré une fenêtre permettant de modifier ses paramètres de recherche. Pour la plupart des gens cette fenêtre n’a qu’un seul usage : configurer Google comme moteur de recherche par défaut.

Quand on s’y attarde un peu, on remarque qu’elle nous propose aussi d’ajouter de nouveaux moteurs de recherche, certains étant prédéfinis (Bing, StartPage, DuckDuckGo), et d’autres correspondant aux sites internet visités récemment.

Sous Google Chrome, la fenêtre est disponible dans Menu > Paramètres > Recherche, et l’ajout des nouveaux moteurs ressemble à ça :

Ajout d'un moteur de recherche personnalisé dans Google Chrome
Ajout de moteurs de recherche personnalisés dans Google Chrome


Une fois un moteur ajouté il peut être marqué comme moteur par défaut (mais bon, espérer détrôner Google là dessus… voila quoi), ou être utilisé à la demande lorsqu’une requête est saisie dans le navigateur.

C’est une fonctionnalité peu connue du grand public, mais appréciée des utilisateurs des sites qui proposent beaucoup de contenu (documentations professionnelles, tutoriels, recettes de cuisine, etc.).

Pour qu’un site internet apparaisse dans la liste, son moteur de recherche doit être détecté par le navigateur lors de son passage. Cette détection est automatique (en anglais on parle d’Autodiscovery) et s’effectue selon le protocole OpenSearch.

Aperçu du protocole OpenSearch

Le protocole OpenSearch est né en 2005, initié par une filiale d’Amazon appelée A9.com. Il ne faut pas le confondre avec le OpenSearch Search Engine Project lancé par Amazon en 2021. En matière de documentation, les deux ressources clés sont la spécification OpenSearch officielle 1.1 ainsi que la documentation d’implémentation OpenSearch de Mozilla.

Pour l’essentiel, le protocole consiste à décrire le(s) moteur(s) de recherche fournis par le site internet dans un ou plusieurs fichier(s) XML de description OpenSearch (en anglais OpenSearchDescription file), et à mettre des balises de liens vers ces fichiers dans le code source des pages web.

Description OpenSearch XML du moteur de recherche

Voici un exemple de fichier XML décrivant un moteur de recherche fictif.
Il contient le strict minimum, la spécification présente d’autres champs qui peuvent être ajoutés si besoin.

Ce fichier peut être nommé comme on veut, mais il est courant d’utiliser opensearch.xml, search.xml, ou opensearchdescription.xml. Il est en général placé à la racine du site web.

<?xml version="1.0" encoding="UTF-8" ?>
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
  <ShortName>Nom du site (16 caractères)</ShortName>
  <LongName>Nom du site (48 caractères)</LongName>
  <Description>Description du moteur de recherche (1024 caractères)</Description>
  <Image>https://example.com/icone_du_moteur.png</Image>
  <Url type="text/html" template="https://example.com/url_de_recherche?s={searchTerms}"/>
</OpenSearchDescription>

Quelques remarques concernant les champs du fichier :

ShortName : Nom court du site, plein texte sans code HTML, d’une longueur maximum de 16 caractères. Obligatoire.

LongName : Nom long du site, plein texte sans code HTML, d’une longueur maximum de 48 caractères. Facultatif.

Description : Description du site, plein texte sans code HTML, d’une longueur maximum de 1024 caractères. Obligatoire mais peut être vide.

Image : URL vers l’icône du site. Doit être au format PNG ou ICO, idéalement (mais pas forcémment) de 16×16 ou 64×64 pixels. Doit faire moins de 10 kilo-octets. Supporte aussi les images data-uri. Facultatif mais recommandé.

Url : URL de la page de recherche. L’attribut type doit être text/html pour la plupart des navigateurs. L’attribut template contient l’URL elle-même, avec la mention {searchTerms} là ou doit s’insérer la requête de l’utilisateur. Obligatoire.

Balise de découverte <link> vers le fichier XML

Il faut indiquer au navigateur où se situe le fichier XML de description du moteur, sans quoi il ne saura pas le trouver tout seul.
Cela se fait en insérant une balise <link> dans la partie <head></head> des pages du site.

Voici un exemple de balise.

<head>
  <!-- […] -->
  <link 
    rel="search" 
    type="application/opensearchdescription+xml" 
    title="Nom du site" 
    href="https://example.com/opensearch.xml"
  />
  <!-- […] -->
</head>

L’attribut title doit contenir le nom du site, et l’attribut href doit contenir l’adresse du fichier de description OpenSearch créé précédemment.
Les valeurs des attributs rel et type sont fixes et ne changent pas en fonction du site.

N’hésitez pas à vous plonger dans la spécification pour voir les autres possibilités d’OpenSearch.
Nous en savons maintenant assez pour passer à l’implémentation du protocole OpenSearch au sein de WordPress.

Ajouter OpenSearch à un site WordPress, sans plugin

Ajout de la description opensearch.xml

On va tout d’abord créer la description XML du moteur de recherche du site. Celle-ci sera servie à l’adresse https://adressedusite/opensearch.xml.

Plutôt que créer physiquement un fichier XML à la racine du site, on va utiliser un fichier virtuel, qui sera créé à la volée par WordPress lors des requêtes des visiteurs. Le CMS propose en effet des fonctions qui facilitent grandement la génération des informations OpenSearch :

⚙️  La fonction get_bloginfo() permet de récupérer le nom du site ainsi que sa description. Parfait pour les champs ShortName, LongName et Description.

⚙️  La fonction get_site_icon_url() permet de récupérer l’adresse de la favicon du site, soit d’une taille donnée, soit plein format. Juste ce qu’il faut pour le champ Image.

⚙️  Pour le champ Url, on considérera que WordPress utilise une adresse sous la forme https://example.com/?s=motcle. Tout ce que nous avons à faire pour obtenir cette URL, c’est ajouter ?s={searchTerms} à l’adresse du site que l’on peut obtenir avec la fonction home_url().

On utilise le hook do_parse_request afin d’injecter notre code dans la procédure d’initialisation de WordPress et répondre aux requêtes pour le fichier opensearch.xml. Ca nous permet de générer dynamiquement son contenu en fonction des réglages du site. Ainsi, toute modification du nom, du favicon ou de la description du site sera automatiquement prise en compte.

Le code suivant peut être ajouté au fichier functions.php du thème enfant du site :

add_action("do_parse_request", function($do_parse){

  // On ne gère que les requêtes pour /opensearch.xml
  if(! isset($_SERVER["REQUEST_URI"]) || strtok($_SERVER["REQUEST_URI"], "?") != "/opensearch.xml") return $do_parse;

  // Champ ShortName : 16 caractères maximum sans HTML
  $shortName = get_bloginfo("name");
  $shortName = html_entity_decode($shortName);
  $shortName = mb_substr($shortName, 0, 16);

  // Champ LongName : 48 caractères maximum sans HTML
  $longName = get_bloginfo("name");
  $longName = html_entity_decode($longName);
  $longName = mb_substr($longName, 0, 48);

  // Champ Description : 1024 caractères maximum sans HTML
  $description = get_bloginfo("description");
  $description = html_entity_decode($description);
  $description = mb_substr($description, 0, 1024);

  // Champ Image : Taille idéale 64x64, maximum 10kb
  $image = get_site_icon_url(64);

  // Champ URL : Par défaut WordPress utilise la page d'accueil avec ?s=motcle
  $url = home_url('/').'?s=';
  
  // Envoi du header HTTP indiquant un contenu XML
  header("Content-Type:application/xml; charset=utf-8");
  
  // Envoi du contenu XML
  echo '<?xml version="1.0" encoding="UTF-8" ?>';
  echo '<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">';
  echo '<ShortName>'.esc_html($shortName).'</ShortName>';
  echo '<LongName>'.esc_html($longName).'</LongName>';
  echo '<Description>'.esc_html($description).'</Description>';
  echo '<Image>'.esc_url($image).'</Image>';
  echo '<Url type="text/html" template="'.esc_url($url).'{searchTerms}"/>';
  echo '</OpenSearchDescription>';

  // Requête terminée, on arrête tous les traitements
  exit;

});

Enregistrez le fichier PHP et rendez-vous avec votre navigateur à l’adresse https://example.com/opensearch.xml (remplacez example.com par le domaine de votre site), vous devriez voir le contenu XML de votre description OpenSearch toute neuve.

Ajout de la balise de découverte <link>

Maintenant que l’on a un fichier de description du moteur de recherche, il nous faut rajouter la balise <link> dans la partie <head> des pages pour indiquer sa présence au navigateur.

On utilise le hook wp_head afin de l’ajouter façon WordPress-style. Ca donne le code suivant, toujours à insérer dans le fichier functions.php :

add_action("wp_head", function(){

  // Ajout balise <link> vers /opensearch.xml
  echo '<link rel="search" type="application/opensearchdescription+xml" title="'.esc_attr(get_bloginfo("name")).'" href="'.esc_url(home_url("/opensearch.xml")).'" />';

});

Une fois le fichier PHP enregistré, chargez une page du site dans votre navigateur et vérifiez le code source HTML, la balise devrait être présente à présent.

Si tout va bien, la fenêtre de réglage des paramètres de recherche du navigateur devrait vous proposer à présent d’ajouter le site aux moteurs de recherches installés.
Si ca n’est pas le cas, vérifiez que le fichier de description XML est correct.

Le code complet

Voici le code complet pour rendre compatible votre site WordPress avec le standard OpenSearch. Il peut être inséré tel quel dans le fichier functions.php de votre thème enfant.

/* Support OpenSearch */

add_action("wp_head", function(){

  // Ajout balise <link> vers /opensearch.xml
  echo '<link rel="search" type="application/opensearchdescription+xml" title="'.esc_attr(get_bloginfo("name")).'" href="'.esc_url(home_url("/opensearch.xml")).'" />';

});

add_action("do_parse_request", function($do_parse){

  // On ne gère que les requêtes pour /opensearch.xml
  if(! isset($_SERVER["REQUEST_URI"]) || strtok($_SERVER["REQUEST_URI"], "?") != "/opensearch.xml") return $do_parse;

  // Champ ShortName : 16 caractères maximum sans HTML
  $shortName = get_bloginfo("name");
  $shortName = html_entity_decode($shortName);
  $shortName = mb_substr($shortName, 0, 16);

  // Champ LongName : 48 caractères maximum sans HTML
  $longName = get_bloginfo("name");
  $longName = html_entity_decode($longName);
  $longName = mb_substr($longName, 0, 48);

  // Champ Description : 1024 caractères maximum sans HTML
  $description = get_bloginfo("description");
  $description = html_entity_decode($description);
  $description = mb_substr($description, 0, 1024);

  // Champ Image : Taille idéale 64x64, maximum 10kb
  $image = get_site_icon_url(64);

  // Champ URL : Par défaut WordPress utilise la page d'accueil avec ?s=motcle
  $url = home_url('/').'?s=';
  
  // Envoi du header HTTP indiquant un contenu XML
  header("Content-Type:application/xml; charset=utf-8");
  
  // Envoi du contenu XML
  echo '<?xml version="1.0" encoding="UTF-8" ?>';
  echo '<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">';
  echo '<ShortName>'.esc_html($shortName).'</ShortName>';
  echo '<LongName>'.esc_html($longName).'</LongName>';
  echo '<Description>'.esc_html($description).'</Description>';
  echo '<Image>'.esc_url($image).'</Image>';
  echo '<Url type="text/html" template="'.esc_url($url).'{searchTerms}"/>';
  echo '</OpenSearchDescription>';

  // Requête terminée, on arrête tous les traitements
  exit;

});

Conclusion

Et voila, pour peu que le site WordPress soit suffisamment récent, on dispose à présent d’une solution légère et facile à déployer pour rajouter le support de OpenSearch et permettre au visiteur de lancer des recherches directement depuis son navigateur.

Les cas « extrêmes » qui pourraient se poser sont un nom de site plus long que 48 caractères, un slogan de site plus long que 1024 caractères ou une favicon plus lourde que 10 kilo-octets… Mais bon soyons honnêtes, ce sont des mauvaises pratiques à la base, notamment pour le SEO (si le nom du site est déjà long, alors imaginez le duo nom de page + nom du site affiché en titre d’onglet) et l’optimisation (si une favicon est déjà lourde, alors imaginez les images pleine-largeur présentes sur le site).

J’espère que cet article vous sera utile. Comme d’habitude, si un problème se présente vous pouvez me contacter depuis la page contact du site.

Bon code à vous, à la prochaine.

Références

La spécification opensearch 1.1 – Dewitt (sur Github)
Description du format opensearch – Mozilla Developers

Photo de profil de Loïc Burtin Code Colibri développeur web à Dijon

A propos de l'auteur

Loïc Burtin

Développeur web indépendant, avec une tendance à couper les plumes en quatre pour que les sites se chargent plus rapidement.

Partagez cet article  =>