En tant qu’utilisateur de Google Chrome, vous avez probablement utilisé des extensions dans ce navigateur. Vous êtes-vous déjà demandé comment elles étaient conçues ou si vous pouviez en créer une ?

Cet article vous guide à travers le processus de création d’une extension Chrome, en particulier une extension qui utilise React et l’API Kinsta pour gérer les extensins sur les sites WordPress hébergés avec Kinsta.

Qu’est-ce qu’une extension Chrome ?

Une extension Chrome est un programme installé dans le navigateur Chrome et qui améliore ses fonctionnalités. Les extensions peuvent aller de simples boutons dans la barre d’outils à des fonctionnalités entièrement intégrées qui interagissent profondément avec votre expérience de navigation.

Comment créer une extension Chrome

La création d’une extension Chrome est similaire au développement d’une application web, mais elle nécessite un fichier au format JSON appelé manifest.json. Ce fichier constitue l’épine dorsale de l’extension, dont il détermine les réglages, les autorisations et les fonctionnalités que vous souhaitez inclure.

Pour commencer, créez un dossier qui contiendra tous les fichiers de votre extension. Ensuite, créez un fichier manifest.json dans ce dossier.

Un fichier manifest.json de base pour une extension Chrome comprend des propriétés clés qui définissent les réglages de base de l’extension. Vous trouverez ci-dessous un exemple de fichier manifest.json comprenant les champs nécessaires à son fonctionnement :

{
  "manifest_version": 3,
  "name": "My Chrome extension",
  "version": "1.0",
  "description": "Here is a description for my Chrome extension."
}

Vous pouvez le charger et le tester en tant qu’extension décompressée dans Chrome. Rendez-vous sur chrome://extensions dans votre navigateur et basculez en mode développeur, puis cliquez sur le bouton Charger l’extension non empaquetée. Cela ouvrira un navigateur de fichiers et vous pourrez sélectionner le répertoire que vous avez créé pour votre extension.

Chargez une extension Chrome en cliquant sur Charger l'extension non empaquetée en mode développeur.
Chargez une extension Chrome en cliquant sur Charger l’extension non empaquetée en mode développeur.

Lorsque vous cliquez sur l’icône de l’extension, rien ne se passe car vous n’avez pas créé d’interface utilisateur.

Créer une interface utilisateur (popup) pour votre extension Chrome

Comme pour toute application web, l’interface utilisateur (UI) de votre extension utilise le HTML pour structurer le contenu, le CSS pour le styliser et le JavaScript pour ajouter de l’interactivité.

Créons une interface utilisateur de base à l’aide de tous ces fichiers. Commencez par créer un fichier HTML (popup.html). Ce fichier définit la structure des éléments de votre interface utilisateur, tels que le texte, les titres, les images et les boutons. Ajoutez le code suivant :

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Hello World</title>
        <link rel="stylesheet" href="popup.css" />
    </head>
    <body>
        <h1>Hello World!</h1>
        <p>My first Chrome Extension</p>
        <button> id="sayHello">Say Hello</button>
        <script> src="popup.js"></script>
    </body>
</html>

Le code ci-dessus crée un titre, un paragraphe et un bouton. Les fichiers CSS et JavaScript sont également liés. Ajoutez maintenant quelques styles dans le fichier popup.css :

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    font-family: Arial, sans-serif;
    background-color: aliceblue;
    padding: 20px;
}

Ensuite, dans le fichier popup.js, ajoutez un récepteur d’évènements au bouton de sorte que lorsqu’il est cliqué, une alerte s’affiche :

const sayHelloBtn = document.getElementById('sayHello');
sayHelloBtn.addEventListener('click', async () => {
    let tab = await chrome.tabs.query({ active: true });
    chrome.scripting.executeScript({
        target: { tabId: tab[0].id },
        function: () => alert('Hello from the extension!'),
    });
});

Ce code JavaScript récupère l’onglet actif actuel et utilise l’API Chrome Scripting pour exécuter un script qui affiche une alerte avec un message d’accueil lorsque le bouton Say Hello est cliqué. Cela introduit une interactivité de base dans votre extension Chrome.

Grâce à ces étapes, vous avez mis en place une interface utilisateur popup simple pour votre extension Chrome, qui comprend un texte, un style et une fonctionnalité de base.

Enfin, vous devez activer le fichier popup dans le fichier manifest.json en ajoutant quelques permissions :

{
    . . . ,
    "action": {
        "default_popup": "popup.html"
    },
    "permissions": [
        "scripting",
        "tabs"
    ],
    "host_permissions": [
        "http://*/*",
        "https://*/*"
    ]
}

Dans la configuration ci-dessus, la clé default_popup spécifie que popup.html sera l’interface utilisateur par défaut lorsque l’utilisateur interagira avec l’extension. Le tableau permissions comprend scripting et tabs, qui sont essentiels pour que l’extension puisse interagir avec les onglets et utiliser les fonctions de script du navigateur.

Le tableau host_permissions indique les sites avec lesquels votre extension peut interagir. Les modèles http://*/* et https://*/* indiquent que votre extension peut interagir avec tous les sites web accessibles via les protocoles HTTP et HTTPS.

Avec ces paramètres dans votre fichier manifest.json, votre extension Chrome est correctement configurée pour afficher une fenêtre contextuelle et exécuter des scripts.

Recharger votre extension Chrome

Une fois ces changements effectués dans votre dossier local, vous devez mettre à jour le dossier décompressé chargé dans Chrome. Pour cela, ouvrez la page des extensions de Chrome, trouvez votre extension et cliquez sur l’icône de rechargement.

Cliquez sur l'icône d'actualisation pour recharger l'extension.
Cliquez sur l’icône d’actualisation pour recharger l’extension.

Vous pouvez ensuite cliquer sur l’icône de l’extension et une fenêtre contextuelle apparait. Lorsque vous cliquez sur le bouton Say Hello, une alerte apparait.

Vous avez maintenant des connaissances de base sur la manière de créer une extension Chrome. Il y a d’autres possibilités. Vous pouvez manipuler l’interface utilisateur de votre site, effectuer des requêtes API, récupérer des données à partir d’URL pour effectuer des opérations spécifiques, et bien plus encore.

Comment créer une extension Chrome avec React

Comme nous l’avons mentionné précédemment, la création d’une extension Chrome est similaire à la création d’une application web. Vous pouvez utiliser des frameworks web populaires comme React.

Pour React, le fichier manifest.json est créé dans le dossier public. Ce dossier est utilisé pour les ressources statiques que vous ne voulez pas voir traités par Webpack (ou des bundlers similaires que React peut utiliser sous le capot dans des outils comme Create React App).

Lorsque vous construisez votre application React, le processus de construction copie tout le contenu du dossier public dans le dossier dist. Voici comment créer une extension Chrome avec React :

  1. Créez une nouvelle application React. Vous pouvez utiliser l’environnement de développement local Vite en exécutant la commande suivante dans votre terminal :
npm create vite@latest

Ensuite, donnez un nom à votre projet et sélectionnez React comme framework. Une fois cela fait, naviguez dans le dossier du projet et installez les dépendances :

cd <project-name>
npm install
  1. Dans le dossier public de votre projet React, créez un fichier manifest.json. Ajoutez les configurations suivantes :
{
    "manifest_version": 3,
    "name": "React Chrome extension",
    "description": "Chrome extension built with React",
    "version": "0.1.0",
    "action": {
        "default_popup": "index.html"
    },
    "permissions": [
        "tabs"
    ],
    "host_permissions": [
        "http://*/*",
        "https://*/*"
    ]
}

La configuration d’une extension Chrome comprend un objet action qui définit index.html comme fenêtre contextuelle par défaut lorsque vous cliquez sur l’icône de l’extension. Il s’agit du fichier HTML statique généré lorsque vous construisez votre application React.

  1. Développez l’application React. N’hésitez pas à faire des requêtes d’API, à les styliser comme vous le souhaitez, à utiliser des hooks React, etc.
  1. Lorsque vous avez terminé la construction de l’interface utilisateur de l’extension, exécutez la commande de construction dans React (npm run build). Toutes vos ressources, y compris votre fichier manifest.json, l’index.html généré par React, et d’autres, sont déplacés dans le dossier dist ou build.
  2. Enfin, chargez votre extension dans Chrome. Naviguez jusqu’à chrome://extensions/ et rechargez votre extension.

Création d’une extension Chrome pour gérer les plugins de votre site avec l’API Kinsta

Voici à quoi ressemblera l’extension Chrome que vous allez créer :

Extension Chrome construite avec React et interagissant avec l'API Kinsta.
Extension Chrome construite avec React et interagissant avec l’API Kinsta.

Lorsque vous cliquez sur l’extension, celle-ci affiche une liste de sites dont les extensions sont obsolètes sur votre compte MyKinsta. Vous pouvez voir la liste des extensions et cliquer sur le bouton Voir dans MyKinsta pour naviguer vers la page Themes & Extensions du site, où vous pouvez mettre à jour chaque extension.

Voyons maintenant comment créer l’extension Chrome.

Comprendre l’API Kinsta

L’API Kinsta est un outil puissant qui vous permet d’interagir de manière programmatique avec les services Kinsta comme les sites WordPress hébergés. Elle peut vous aider à automatiser diverses tâches liées à la gestion de WordPress, y compris la création de sites, la récupération d’informations sur les sites, l’obtention de l’état d’un site, la consultation et la restauration de sauvegardes, et plus encore.

Pour utiliser l’API de Kinsta, vous devez avoir un compte avec au moins un site, une application ou une base de données WordPress dans MyKinsta. Vous devez également générer une clé API pour vous authentifier et accéder à votre compte.

Pour générer une clé API :

  1. Allez sur votre tableau de bord MyKinsta.
  2. Naviguez jusqu’à la page des clés API (Votre nom > Réglages de l’entreprise > Clés API).
  3. Cliquez sur Créer une clé API.
  4. Choisissez une date d’expiration ou définissez une date de début personnalisée et un nombre d’heures pour l’expiration de la clé.
  5. Donnez un nom unique à la clé.
  6. Cliquez sur Générer.

Après avoir créé une clé API, copiez-la et conservez-la en lieu sûr (il est recommandé d’utiliser un gestionnaire de mots de passe). Vous pouvez générer plusieurs clés API, qui seront répertoriées sur la page Clés API. Si vous devez révoquer une clé API, cliquez sur le bouton Révoquer.

Gérer les plugins de votre site avec l’API Kinsta et React

Commençons par développer une interface utilisateur dans React, qui sera ensuite transformée en une extension Chrome. Ce guide suppose une connaissance de base de React et de l’interaction avec l’API.

Mise en place de l’environnement

Tout d’abord, dans le fichier App.jsx, définissez une constante pour l’URL de l’API Kinsta afin d’éviter toute redondance dans votre code :

const KinstaAPIUrl = 'https://api.kinsta.com/v2';

Pour des raisons de sécurité, stockez les données sensibles telles que votre clé API et l’identifiant de la société Kinsta dans un fichier .env.local pour les garder en sécurité et hors de votre code source :

VITE_KINSTA_COMPANY_ID=YOUR_COMPANY_ID
VITE_KINSTA_API_KEY=YOUR_API_KEY

Récupérer des données avec l’API Kinsta

Dans le fichier App.jsx, vous devez faire plusieurs demandes à l’API de Kinsta pour récupérer des informations sur les sites et les extensions.

  1. Récupérer les sites de l’entreprise : Commencez par récupérer la liste des sites associés à votre compte d’entreprise Kinsta. Utilisez l’identifiant de l’entreprise dans une requête GET, qui renvoie un tableau de détails sur les sites.
    const getListOfCompanySites = async () => {
          const query = new URLSearchParams({
            company: import.meta.env.VITE_KINSTA_COMPANY_ID,
          }).toString();
          const resp = await fetch(`${KinstaAPIUrl}/sites?${query}`, {
            method: 'GET',
            headers: {
              Authorization: `Bearer ${import.meta.env.VITE_KINSTA_API_KEY}`,
            },
          });
          const data = await resp.json();
          const companySites = data.company.sites;
          return companySites;
        }
  2. Récupérer les données d’environnement pour chaque site : Pour chaque site, récupérez les environnements, qui comprennent l’identifiant de l’environnement nécessaire pour les requêtes ultérieures. Pour cela, il convient de mapper chaque site et d’effectuer un appel API au point de terminaison /sites/${siteId}/environments.
     const companySites = await getListOfCompanySites();
        // Get all environments for each site
    
        const sitesEnvironmentData = companySites.map(async (site) => {
          const siteId = site.id;
          const resp = await fetch(`${KinstaAPIUrl}/sites/${siteId}/environments`, {
            method: 'GET',
            headers: {
              Authorization: `Bearer ${import.meta.env.VITE_KINSTA_API_KEY}`,
            },
          });
          const data = await resp.json();
          const environments = data.site.environments;
          return {
            id: siteId,
            name: site.display_name,
            environments: environments,
          };
        });
  3. Récupérer les extensions pour chaque environnement de site : Enfin, utilisez l’identifiant de l’environnement pour récupérer les extensions pour chaque site. Cette étape implique une fonction de mappage et un appel API au point de terminaison /sites/environments/${environmentId}/plugins pour chaque environnement.
    // Wait for all the promises to resolve
        const sitesData = await Promise.all(sitesEnvironmentData);
    
        // Get all plugins for each environment
        const sitesWithPlugin = sitesData.map(async (site) => {
          const environmentId = site.environments[0].id;
          const resp = await fetch(
            `${KinstaAPIUrl}/sites/environments/${environmentId}/plugins`,
            {
              method: 'GET',
              headers: {
                Authorization: `Bearer ${import.meta.env.VITE_KINSTA_API_KEY}`,
              },
            }
          );
          const data = await resp.json();
          const plugins = data.environment.container_info;
          return {
            env_id: environmentId,
            name: site.name,
            site_id: site.id,
            plugins: plugins,
          };
        });

    Vous pouvez maintenant rassembler toutes ces requêtes dans une fonction qui est utilisée pour renvoyer le tableau final des sites avec des détails de base sur chaque site et ses extensions :

    const getSitesWithPluginData = async () => {
      const getListOfCompanySites = async () => {
        const query = new URLSearchParams({
          company: import.meta.env.VITE_KINSTA_COMPANY_ID,
        }).toString();
        const resp = await fetch(`${KinstaAPIUrl}/sites?${query}`, {
          method: 'GET',
          headers: {
            Authorization: `Bearer ${import.meta.env.VITE_KINSTA_API_KEY}`,
          },
        });
        const data = await resp.json();
        const companySites = data.company.sites;
        return companySites;
      }
    
      const companySites = await getListOfCompanySites();
    
      // Get all environments for each site
      const sitesEnvironmentData = companySites.map(async (site) => {
        const siteId = site.id;
        const resp = await fetch(`${KinstaAPIUrl}/sites/${siteId}/environments`, {
          method: 'GET',
          headers: {
            Authorization: `Bearer ${import.meta.env.VITE_KINSTA_API_KEY}`,
          },
        });
        const data = await resp.json();
        const environments = data.site.environments;
        return {
          id: siteId,
          name: site.display_name,
          environments: environments,
        };
      });
    
      // Wait for all the promises to resolve
      const sitesData = await Promise.all(sitesEnvironmentData);
    
      // Get all plugins for each environment
      const sitesWithPlugin = sitesData.map(async (site) => {
        const environmentId = site.environments[0].id;
        const resp = await fetch(
          `${KinstaAPIUrl}/sites/environments/${environmentId}/plugins`,
          {
            method: 'GET',
            headers: {
              Authorization: `Bearer ${import.meta.env.VITE_KINSTA_API_KEY}`,
            },
          }
        );
        const data = await resp.json();
        const plugins = data.environment.container_info;
        return {
          env_id: environmentId,
          name: site.name,
          site_id: site.id,
          plugins: plugins,
        };
      });
    
      // Wait for all the promises to resolve
      const sitesWithPluginData = await Promise.all(sitesWithPlugin);
      return sitesWithPluginData;
    }

Afficher les données du site

Créez un état avec le hook useState pour stocker les sites dont les extensions sont obsolètes. Le hook useEffect appellera également la méthode getSitesWithPluginData() et extraira les détails du site lorsque le composant sera monté.

Dans le hook useEffect, créez une fonction qui passera en boucle sur chaque site pour filtrer les sites dont les extensions sont obsolètes et les stocker dans l’état :

const [sitesWithOutdatedPlugin, setSitesWithOutdatedPlugin] = useState([]);
const [isLoading, setIsLoading] = useState(true);

useEffect(() => {
  const checkSitesWithPluginUpdate = async () => {
    const sitesWithPluginData = await getSitesWithPluginData();
    const sitesWithOutdatedPlugin = sitesWithPluginData.map((site) => {
      const plugins = site.plugins.wp_plugins.data;
      const outdatedPlugins = plugins.filter((plugin) => plugin.update === "available");
      if (outdatedPlugins.length > 0) {
        const kinstaDashboardPluginPageURL = `https://my.kinsta.com/sites/plugins/${site.site_id}/${site.env_id}?idCompany=${import.meta.env.VITE_KINSTA_COMPANY_ID}`;
        return {
          name: site.name,
          plugins: outdatedPlugins,
          url: kinstaDashboardPluginPageURL,
        };
      }
    });

    setSitesWithOutdatedPlugin(sitesWithOutdatedPlugin);

  checkSitesWithPluginUpdate();
  setIsLoading(false);
}, []);

Dans le code ci-dessus, vous remarquez que l’état de chargement est également créé et défini sur true par défaut. Il sera utilisé pour contrôler l’affichage des données. Lorsque toutes les données sont chargées, nous le fixons à false.

Vous trouverez ci-dessous un balisage permettant de rendre les données du site et les extensions dans votre interface utilisateur.

import { useEffect, useState } from "react"
import KinstaLogo from './assets/kinsta-logo.png'
import PluginPage from './components/PluginsPage'

function App() {
  // load the data from the API
  return (
    <div className="container">
        <div>
          <div> className="title-section">
            <img src={KinstaLogo} className="logo" alt="" />
          </div>
          <p> className="info-box">
            Get quick information about your site plugins that need update.
          </p>
          {isLoading ? (
            <p>Loading...</p>
          ) : (
            <>
              <div className="content">
                <p>The following sites have plugins that need to be updated.</p>
                {sitesWithOutdatedPlugin.map((site, index) => {
                  return (
                    <PluginPage key={index} {...site} />
                  );
                })}
              </div>
            </>
          )}
        </div>
    </div>
  )
}
export default App

Le code comprend un en-tête avec un logo et un paragraphe d’information. Le contenu de l’interface utilisateur est rendu de manière conditionnelle en fonction de l’état de isLoading. Si les données sont encore en cours de chargement, un message de chargement est affiché. Une fois les données chargées, l’interface présente les données relatives aux sites et aux éventuelles extensions nécessitant des mises à jour.

Vous remarquerez également un composant : PluginPage (PluginPage.jsx). Ce composant est conçu pour afficher les sites individuels et les détails de leurs extensions. Il comprend une fonctionnalité permettant de basculer la visibilité des détails de l’extension.

import { useState } from "react"
import { FaRegEye } from "react-icons/fa";
import { FaRegEyeSlash } from "react-icons/fa";

const PluginUse = (site) => {
    const [viewPlugin, setViewPlugin] = useState(false);

    return (
        <>
            <div className="site-card">
                <div className="site-card-details">
                    <p>{site.name}</p>
                    <div className="both-btns">
                        <a> href={site.url} target="_blank" rel="noreferrer" className="btn">
                            View in MyKinsta
                        </a>
                        <button onClick={() => setViewPlugin(!viewPlugin)} className="btn" title="View Plugins">
                            {viewPlugin ? <FaRegEyeSlash /> : <FaRegEye />}
                        </button>
                    </div>
                </div>
                {viewPlugin && (
                    <div className="plugin-list">
                        {site.plugins.map((plugin, index) => {
                            return (
                                <div key={index} className="plugin-card">
                                    <p>{plugin.name}</p>
                                    <div className="plugin-version-info">
                                        <p>Current Version: {plugin.version}</p>
                                        <p>Latest Version: {plugin.update_version}</p>
                                    </div>
                                </div>
                            );
                        })}
                    </div>
                )}
            </div>
        </>
    )
}
export default PluginUse

Configurer le fichier manifest

Pour transformer votre interface utilisateur et vos fonctionnalités en une extension Chrome, vous devez configurer le fichier manifest.json.

Créez un fichier manifest.json dans le dossier public et collez le code ci-dessous :

{
    "manifest_version": 3,
    "name": "Kinsta Plugins Manager - Thanks to Kinsta API",
    "description": "This extension allows you to manage your WordPress site's plugin from Kinsta's MyKinsta dashboard via Kinsta API.",
    "version": "0.1.0",
    "icons": {
        "48": "kinsta-icon.png"
    },
    "action": {
        "default_popup": "index.html"
    },
    "permissions": [
        "tabs"
    ],
    "host_permissions": [
        "https://my.kinsta.com/*"
    ]
}

Veillez à ajouter le fichier d’icônes à votre dossier public.

À ce stade, vous pouvez maintenant exécuter la commande build (npm run build) pour que toutes vos ressources, y compris votre fichier manifest.json, l’index.html généré par React et d’autres fichiers, soient déplacés dans le dossier dist ou build.

Ensuite, naviguez jusqu’à chrome://extensions/ et chargez-la en tant qu’extension décompressée dans Chrome. Cliquez sur le bouton Charger l’extension non empaquetée et sélectionnez le répertoire que vous avez créé pour votre extension.

Limiter l’extension à des sites spécifiques

Vous avez remarqué que cette extension fonctionne à tout moment. Nous voulons qu’elle ne fonctionne que lorsqu’un utilisateur navigue vers le tableau de bord MyKinsta.

Pour cela, ajustons le fichier App.jsx. Créez un état pour stocker l’onglet actif :

const [activeTab, setActiveTab] = useState(null);

Ensuite, mettez à jour le hook useEffect pour définir et invoquer la fonction getCurrentTab:

const getCurrentTab = async () => {
  const queryOptions = { active: true, currentWindow: true };
  const [tab] = await chrome.tabs.query(queryOptions);
  setActiveTab(tab);
}
getCurrentTab();

Le code ci-dessus utilise chrome.tabs.query avec des options de requête spécifiques pour s’assurer qu’il ne récupère que l’onglet actif dans la fenêtre actuelle. Une fois l’onglet récupéré, il est défini comme onglet actif dans l’état de l’extension.

Enfin, mettez en œuvre une logique de rendu conditionnel dans l’instruction de retour de votre composant. Cela permet de s’assurer que l’interface de gestion de l’extension n’apparaît que lorsque l’utilisateur est sur le tableau de bord MyKinsta :

return (
  <div className="container">
    {activeTab?.url.includes('my.kinsta.com') ? (
      <div >
        <div className="title-section">
          <img src={KinstaLogo} className="logo" alt="" />
        </div>
        <p className="info-box">
          Get quick information about your site plugins that need update.
        </p>
        {isLoading ? (
          <p>Loading...</p>
        ) : (
          <>
            <div className="content">
              <p>The following {sitesWithPluginUpdate} sites have plugins that need to be updated.</p>
              {sitesWithOutdatedPlugin.map((site, index) => {
                return (
                  <PluginPage key={index} {...site} />
                );
              })}
            </div >
          </>
        )}
      </div >
    ) : (
      <div >
        <div className="title-section">
          <img src={KinstaLogo} className="logo" alt="" />
        </div>
        <p className="info-box">
          This extension is only available on Kinsta Dashboard.
        </p>
      </div>
    )}
  </div>
)

Après avoir effectué les modifications, reconstruisez votre application et rechargez l’extension Chrome. Cela appliquera la nouvelle logique et les nouvelles restrictions.

Résumé

Dans cet article, vous avez appris les bases de la création d’une extension Chrome et comment en créer une avec React. Vous avez également appris à créer une extension qui interagit avec l’API Kinsta.

En tant qu’utilisateur de Kinsta, vous pouvez profiter de l’énorme potentiel et de la flexibilité de l’API Kinsta qui vous aide à développer des solutions personnalisées pour gérer vos sites, vos applications et vos bases de données.

Quel point de terminaison de l’API Kinsta avez-vous beaucoup utilisé, et comment l’avez-vous utilisé ? Partagez avec nous dans la section des commentaires !

Joel Olawanle Kinsta

Joel is a Frontend developer working at Kinsta as a Technical Editor. He is a passionate teacher with love for open source and has written over 200 technical articles majorly around JavaScript and it's frameworks.