À l’occasion d’un laboratoire de Geneanet, à savoir une semaine consacrée au développement de projets innovants, nous avons eu l’envie de réaliser une extension pour WordPress.

La volonté première était de toucher nos utilisateurs blogueurs : nous voulions leur offrir un outil élégant et pratique pour illustrer leurs articles grâce aux données de Geneanet.
Nous souhaitions également l’utiliser pour le blog de Geneanet.
En complément, cela pouvait nous apporter une plus grande visibilité sur les blogs généalogiques et des retombées en terme de SEO.

L’extension réalisée permet d’insérer très facilement dans un article une petite fiche contenant les informations généalogiques principales d’un individu d’un arbre se trouvant sur Geneanet.

Nos graphistes ergonomes se sont attelés à nous créer une maquette pouvant s’intégrer à de nombreux blogs sans se distinguer de la charte utilisée, agréable à l’œil et clair dans l’affichage des informations.
Le résultat final nous a beaucoup plu.

Exemple dans un article de blog

Exemple dans un article de blog

La voir, la télécharger, l’utiliser, contribuer

Give it to me

Pour avoir un exemple de rendu de l’extension, vous pouvez visiter la page suivante (en bas de l’article).

Pour le télécharger, c’est par là.

Pour plus de détails sur son utilisation, vous pouvez visiter la page de Geneanet ou bien celle de GénéaTom, très complète (merci à lui).

Le code source est visible ici et sur GitHub (n’hésitez pas à contribuer si l’envie vous en prend) !

Le fonctionnement général

Rainbow

Nous avons voulu développer une extension de qualité, organisée et maintenable. Nous avons donc choisi d’utiliser un boilerplate (WordPress Plugin Boilerplate) utilisé par de nombreuses extensions et notamment Beautiful taxonomy filters dont nous nous sommes inspirés.
Bien évidemment, nous avons respecté les règles de codage de WordPress en se servant notamment des règles PHP_CodeSniffer créées par WordPress.

L’extension, ainsi que son code source, est découpée en deux parties : la partie visible sur l’article (publique) et la partie visible dans l’interface d’administration.

L’extension se base sur l’utilisation de shortcode, une syntaxe permettant d’insérer des macros dans un article. Voici son utilisation dans notre cas :

[geneanet-embed-individual url="http://gw.geneanet.org/samlap?lang=fr&p=ada&n=byron&oc=0&type=tree" link_type="tree" align="center" /]

Le paramètre url permet de spécifier l’URL d’une personne dans un arbre de Geneanet. Elle contient toutes les informations dont nous avons besoin pour réaliser un appel API permettant de récupérer ses informations.

La partie publique

Lorsque l’article est chargé, WordPress détecte le shortcode et appelle la méthode permettant de remplacer celui-ci par son contenu (embed_individual_shortcode).

De plus, lorsque l’extension est installée, des fichiers CSS et JavaScript spécifiques sont chargés sur les pages du blog.

Afin de s’assurer que le CSS de l’extension ne touche que celle-ci, les classes CSS utilisées sont préfixées par geneanet-embedded-individual.

Le fichier JavaScript apporte une variable globale GeneanetEmbeddedIndividual contenant des méthodes privées (grâce à l’utilisation d’une closure) et une méthode publique : loadGeneanetEmbeddedIndividual.

Le contenu du shortcode possède une balise script. Celle-ci se charge d’appeler la méthode loadGeneanetEmbeddedIndividual lorsque le DOM est chargé en lui donnant notamment son ID unique, permettant ainsi de charger plusieurs shortcodes pour un même article.

La méthode loadGeneanetEmbeddedIndividual réalise alors un appel API permettant de récupérer puis d’afficher les informations de la personne. Si une erreur survient lors de cet appel, un message d’erreur est affiché.

Exemple d'erreur lors du chargement

Exemple d'erreur lors du chargement

La partie administration

Cette partie s’occupe principalement d’analyser l’URL fournie au shortcode et de valider les informations récupérées.

Exemple lors de l'édition d'un article

Exemple lors de l'édition d'un article

Cette opération est effectuée lors de l’enregistrement de l’article, en se servant des hooks définis par WordPress, et plus particulièrement de l’action hook save_post (en se servant de la fonction add_action).

Les hooks constituent le principal moyen pour les extensions de se greffer à WordPress : ce sont des moments précis dans l’exécution de WordPress pour lesquels des fonctions préenregistrées sont appelées.
L’ensemble des hooks forment l’API Plugin de WordPress. Écrire une extension complexe nécessite ainsi de se plonger dans les entrailles du fonctionnement de WordPress mais offre en contrepartie une grande liberté.

L’URL est donc validée, analysée et les informations nécessaires pour faire l’appel API sont récupérées.
Particularité de Geneanet : nous pouvons nous servir pour désigner une personne dans un arbre soit d’un index unique soit d’un NPOC (une référence unique constituée du Nom, du Prénom et de l’Occurrence). Afin de simplifier le code de l’extension nous nous servons seulement de l’index. S’il n’est pas trouvé dans l’URL, un appel API est préalablement fait pour récupérer l’index.
L’appel API principal est ensuite réalisé, notamment pour valider les informations reçues.

Si une erreur survient, que ce soit lors de l’analyse de l’URL ou lors des différents appels API (personne privée par exemple), le reste des opérations pour le shortcode courant est annulé et un transient contenant l’erreur est défini.

Les transients sont un moyen dans WordPress de conserver temporairement des données. Cette fonctionnalité peut notamment servir pour conserver des données entre des rechargements de pages. Plus d’informations est disponible dans l’API Transients.

Dans notre cas, enregister un article recharge la page. Nous conservons donc les erreurs puis nous les affichons en se servant du hook admin_notices.

Exemple d'erreur

Exemple d'erreur

La partie administration permet également d’enregistrer le shortcode pour l’extension Shortcake. Cette extension permet d’ajouter une interface graphique pour l’utilisation des shortcodes.

Exemple de l'interface graphique pour insérer un shortcode

Exemple de l'interface graphique pour insérer un shortcode

Les particularités

La sauvegarde des informations de base

L’extension dans la partie publique ne récupère pas l’ensemble des informations d’une personne en JavaScript. En effet, lors de l’affichage du shortcode dans un article, le nom, le prénom et le sexe de la personne sont déjà présents, avant même que l’appel API en JavaScript ne soit réalisé.

L’intérêt principal est de ne pas afficher un bloc vide lors du chargement de la page : les informations de base sont affichées et un spinner de chargement invite le visiteur à patienter pendant le chargement du reste. Le chargement en devient plus élégant. En cas d’erreur lors de l’appel API, cela permet aussi d’afficher un message contextualisé.

Exemple lors du chargement

Exemple lors du chargement

Ces informations de base ne changent jamais, ce qui nous permet de les conserver.

C’est lors de l’enregistrement de l’article et de l’appel API qui y est réalisé que ces informations sont récupérées. Ces informations sont sauvegardées en base de données en servant d’une option de WordPress.

L’API Options permet de stocker très facilement des données dans la base de données de WordPress. Elle s’apparente à l’API Transients que nous avons vue, la seule différence étant que les données sont conservées de manière permanente.

Vous vous êtes peut-être demandé·e comment la partie publique du shortcode avait accès à l’index de la personne pour réaliser l’appel API en JavaScript, ou bien comment son ID était généré. C’est la même option qui est utilisée pour conserver ces informations (l’ID unique est généré en PHP en se servant des paramètres du shortcode).

Vous pouvez également remarquer que le lien vers l’arbre (ou la fiche) de la personne est lui aussi présent avant le chargement. Ce lien est généré après l’analyse de l’URL passée en paramètre du shortcode et dépend du type de lien (link_type) choisi.

L’éditeur visuel

L’éditeur visuel est une fonctionnalité de WordPress très pratique : elle permet de prévisualiser en temps réel l’article que nous sommes en train d’écrire.

Exemple de l'éditeur visuel

Exemple de l'éditeur visuel

Rendre l’extension compatible avec celui-ci (seulement lorsque l’extension Shortcake est installée) a été un vrai casse-tête. De nombreux problèmes se sont posés :

  1. Comment rendre la partie publique du code de l’extension accessible à la partie administration en conservant le principe de Separation of Concerns ?
  2. Comment charger le shortcode sans option enregistrée (et donc sans donnée) dans la partie publique ?
  3. Comment faire en sorte que les erreurs gérées lors de l’enregistrement de l’article le soient dans ce cas ?
  4. La prévisualisation du shortcode est faite dans une iframe. Comment faire pour que le JavaScript puisse y accéder ?

Nous n’allons pas rentrer dans les détails, mais exposons simplement les solutions trouvées :

  1. Nous avons ajouté un getter de la partie publique dans la partie administration. Si l’utilisateur se trouve sur la page de modification d’un article, les hooks de la partie publique sont enregistrés. Pour plus d’informations voir la méthode define_admin_hooks.
  2. Il est possible de savoir que nous nous trouvons dans le cas de l’éditeur visuel en se servant de la condition 'wp_ajax_bulk_do_shortcode' === current_filter(). Dès lors, si nous sommes dans ce cas, lors du remplacement du shortcode par son contenu, nous appelons la partie administration en se servant d’un getter pour récupérer les données. Voir ici pour plus d’informations.
  3. Dans le même ordre idée, si les données n’ont pas été correctement récupérées, c’est qu’une erreur s’est produite dans la partie administration. Nous remplaçons alors le contenu du shortcode par un message d’erreur.
  4. Pour qu’à la fois l’iframe et une page classique puissent accéder à l’objet JavaScript GeneanetEmbeddedIndividual, nous utilisons window.top dans la balise script du contenu du shortcode.
Exemple d'erreur dans l'éditeur visuel

Exemple d'erreur dans l'éditeur visuel

API Geneanet : cross-domain et origine

L’API de Geneanet est utilisée pour récupérer les informations de la personne. C’est donc une API externe appelée en cross-domain (CORS).

Du côté client, il nous a simplement suffi d’ajouter le paramètre crossDomain à true dans la fonction ajax de jQuery.

La partie serveur a demandé plus de travail. Ce travail avait heureusement déjà été en partie réalisé pour d’autres API de Geneanet. Nous avons configuré Symfony pour que les routes API externes soient regroupées dans chaque bundle dans un fichier de routing dédié. Ces fichiers sont inclus dans un fichier parent. Celui-ci est lui-même inclus en tant que ressource dans le fichier de routing global de Geneanet qui lui définit le host api.geneanet.org.

Nous pouvons ainsi configurer indépendamment la sécurité de l’host (qui est notamment stateless). Nous utilisions avant le développement de cette extension un EventListener maison pour gérer le cross-domain. Étant donné nos nouveaux besoins (gérer une configuration CORS fine par route et par host), nous avons choisi de nous servir de l’excellent NelmioCorsBundle.

Les appels API de l’extension ajoutent un paramètre origin. Cela nous permet du côté de Geneanet de mesurer l’utilisation de l’extension et notamment de son impact sur les performances de nos serveurs. Nous utilisons pour cela Grafana.

Affichage des appels sur Grafana

Affichage des appels sur Grafana

Localisation de l’extension

L’extension est traduite dans 11 langues. WordPress permet de traduire très facilement les extensions, en se servant de la librairie gettext. Nous avons généré nos fichiers de traduction en se servant de Poedit, qui permet de parcourir le code de l’extension et d’y extraire les chaînes de caractères à traduire.

Ces chaînes de caractères sont traduites grâce à de nombreuses fonctions offertes par WordPress, qui permettent notamment de gérér la pluralisation ou la désambiguïsation par contexte. Plus d’informations ici.

Relevons simplement la fonction wp_localize_script, très utile dans notre cas puisqu’elle permet de générer un objet JavaScript contenant les traductions.

Cet objet nous a néanmoins posé problème dans le cas du blog de Geneanet. Nous n’utilisons en effet pas directement WordPress, mais nous passons par son API REST. Nous avons donc été dans l’obligation de créer une nouvelle route API nous retournant les traductions JavaScript. Cette route utilise principalement la fonction wp_scripts pour récupérer le script de localisation. Elle le parse pour en extraire les données puis les retourne sous forme de JSON.

Conclusion

Le développement de cette extension nous a permis d’utiliser beaucoup de fonctionnalités de l’API WordPress : shortcode, hook, transient, option.

De prime abord assez simple, de nombreuses problématiques se sont posées qui ont été très intéressantes à relever.

Chi

Nous espérons que cette extension et sa réalisation vous plaisent autant qu’à nous !

Et ensuite ?

Nous avons pensé à quelques pistes d’amélioration pour les évolutions de cette extension :

  • Afficher en option la chronologie de la personne.
  • Afficher en option un arbre réduit.

Vous voulez nous y aider ? N’hésitez pas à vous exprimer sur le forum de Geneanet, ou bien nous faire une Pull Request sur GitHub.

Commentaires