// Crée un objet global nommé fdi pour regrouper les fonctions liées à la recherche SharePoint ; bonne pratique : éviter de disperser les fonctions dans le scope global ; limite : un objet global peut être écrasé si un autre script utilise le même nom. let fdi = { // Stocke la dernière URL de requête exécutée ; utile pour le debug ou pour rejouer une requête ; limite : cette valeur est écrasée à chaque nouvelle recherche. lastQuery: null, // Stocke le dernier résultat brut renvoyé par SharePoint ; utile pour analyser la réponse complète ; limite : peut consommer de la mémoire si le résultat est volumineux. lastResult: null, // Déclare une fonction asynchrone de recherche SharePoint ; bonne pratique : utiliser async/await pour rendre le code plus lisible ; limite : nécessite un navigateur moderne. async search(siteUrl, query, targetId, selectProperties, rowLimit = 500, specialFields = [], startrow = 0) { // Construit l’URL de l’API Search SharePoint ; bonne pratique : centraliser la construction de la requête ; limite : une URL trop longue peut poser problème selon les navigateurs ou proxies. const requestUrl = // Ajoute l’endpoint REST de recherche SharePoint au site fourni ; bonne pratique : passer l’URL du site en paramètre pour rendre la fonction réutilisable ; limite : l’utilisateur doit avoir les droits sur ce site. `${siteUrl}/_api/search/query` + // Ajoute le texte de recherche KQL à la requête ; bonne pratique : encoder la valeur pour éviter les erreurs de caractères spéciaux ; limite : encodeURIComponent ne remplace pas une validation fonctionnelle de la requête KQL. `?querytext='${encodeURIComponent(query)}'` + // Ajoute la liste des propriétés managées à retourner ; bonne pratique : ne sélectionner que les champs nécessaires pour améliorer les performances ; limite : les propriétés doivent être bien déclarées comme managed properties dans SharePoint Search. `&selectproperties='${encodeURIComponent(selectProperties)}'` + // Ajoute un tri décroissant sur la date de dernière modification ; bonne pratique : présenter les contenus récents en premier ; limite : LastModifiedTime doit être disponible dans l’index de recherche. `&sortlist='Size:descending'` + // Limite le nombre de résultats retournés à 500 ; bonne pratique : éviter de remonter un volume illimité ; limite : 500 peut être trop élevé pour l’affichage direct dans un navigateur. `&rowlimit=${rowLimit}&startrow=${startrow}`; console.log(requestUrl); // Démarre un bloc try/catch pour gérer proprement les erreurs ; bonne pratique : toujours prévoir les erreurs réseau ou API ; limite : certaines erreurs SharePoint peuvent rester génériques côté client. try { // Affiche l’URL appelée dans la console pour faciliter le diagnostic ; bonne pratique : utile en développement ; limite : éviter de laisser trop de logs en production. console.log("Search URL :", requestUrl); // Exécute l’appel HTTP vers SharePoint avec fetch ; bonne pratique : préférer fetch à XMLHttpRequest pour un code moderne ; limite : fetch dépend du contexte d’authentification SharePoint courant. const response = await fetch(requestUrl, { // Définit la méthode HTTP utilisée ; bonne pratique : GET est adapté pour une requête de lecture ; limite : les URL GET peuvent devenir longues avec beaucoup de paramètres. method: "GET", // Définit les en-têtes HTTP envoyés à SharePoint ; bonne pratique : demander une réponse JSON moderne ; limite : SharePoint peut renvoyer une structure différente selon le mode OData. headers: { // Demande une réponse JSON sans métadonnées OData verboses ; bonne pratique : réduit la taille de la réponse ; limite : le parsing diffère de l’ancien mode odata=verbose. "Accept": "application/json;odata=nometadata" } }); // Vérifie si la réponse HTTP indique un succès ; bonne pratique : ne pas supposer qu’un fetch réussi signifie une réponse applicative valide ; limite : response.ok ne donne pas toujours le détail métier de l’erreur. if (!response.ok) { // Déclenche une erreur explicite avec le code HTTP ; bonne pratique : faciliter le debug ; limite : le message reste générique sans lecture détaillée du corps de réponse. throw new Error(`Erreur SharePoint : statut HTTP ${response.status}`); } // Convertit la réponse HTTP en objet JavaScript JSON ; bonne pratique : travailler ensuite sur un objet plutôt que du texte brut ; limite : échoue si SharePoint ne renvoie pas du JSON valide. const result = await response.json(); // Mémorise l’URL de la dernière requête exécutée ; bonne pratique : utile pour audit ou debug ; limite : ne constitue pas un historique complet des appels. this.lastQuery = requestUrl; // Mémorise le résultat brut de la dernière recherche ; bonne pratique : permet d’inspecter la réponse complète si besoin ; limite : peut contenir beaucoup d’informations inutiles pour l’affichage. this.lastResult = result; // Récupère la liste des lignes de résultats dans la réponse SharePoint ; bonne pratique : utiliser l’optional chaining pour éviter les erreurs si une propriété est absente ; limite : si la structure API change, rows sera vide. const rows = result.PrimaryQueryResult?.RelevantResults?.Table?.Rows || []; // Découpe la chaîne des propriétés sélectionnées en tableau de colonnes ; bonne pratique : travailler sur un tableau pour faciliter les traitements ; limite : suppose que les propriétés sont séparées par des virgules. const columns = selectProperties // Sépare les propriétés à partir des virgules ; bonne pratique : format simple et lisible ; limite : ne supporte pas les noms contenant eux-mêmes une virgule. .split(",") // Nettoie les espaces autour de chaque propriété ; bonne pratique : évite les erreurs dues à " Title" au lieu de "Title" ; limite : ne corrige pas les fautes de nom de propriété. .map(column => column.trim()); // Transforme chaque ligne SharePoint en objet JavaScript simple ; bonne pratique : rendre les données faciles à afficher ou exporter ; limite : les valeurs complexes restent sous forme de chaîne. let lastFileName = ""; let lastSize = 0; const data = rows.map((row, index) => { // Crée un objet représentant une ligne de résultat ; bonne pratique : construire un modèle clair pour l’affichage ; limite : cet objet ne contient que les colonnes demandées. const item = { // Ajoute le numéro de ligne dans le résultat local ; bonne pratique : facilite le repérage visuel ; limite : ce numéro n’est pas l’identifiant SharePoint. //rowNum: index, doublon: false }; // Prépare un dictionnaire temporaire des cellules SharePoint ; bonne pratique : évite une triple boucle coûteuse ; limite : si deux cellules ont la même clé, la dernière écrase la précédente. const cellMap = {}; // Parcourt les cellules renvoyées par SharePoint pour la ligne courante ; bonne pratique : convertir un tableau Key/Value en objet plus simple ; limite : suppose que row.Cells existe bien. row.Cells.forEach(cell => { // Ajoute la valeur de chaque cellule dans le dictionnaire en utilisant la clé SharePoint ; bonne pratique : accès rapide par nom de propriété ; limite : les valeurs nulles ou complexes ne sont pas transformées. if (isNaN(Number(cell.Value))) cellMap[cell.Key] = cell.Value; else { if (cell.Key === "Size") { let size = Number((Number(cell.Value) / 1024 / 1024).toFixed(2)); // Convertit la taille en Mo cellMap[cell.Key] = size; if (lastFileName === cellMap["Filename"] && lastSize === size) { item.doublon = true; } lastFileName = cellMap["Filename"]; lastSize = size; } else cellMap[cell.Key] = Number(cell.Value); } }); // Parcourt les colonnes demandées par l’utilisateur ; bonne pratique : garantir que chaque objet contient les mêmes propriétés ; limite : les colonnes inexistantes seront vides. columns.forEach(column => { // Alimente la propriété demandée avec la valeur trouvée ou une chaîne vide ; bonne pratique : évite les undefined dans l’affichage ; limite : une vraie valeur falsy comme 0 peut être remplacée par vide avec ||. item[column] = cellMap[column] || ""; }); // Retourne l’objet correspondant à une ligne transformée ; bonne pratique : garder la transformation pure et lisible ; limite : aucun enrichissement métier n’est effectué ici. return item; }); // Affiche les données sous forme de tableau dans la console ; bonne pratique : très lisible en debug ; limite : à éviter en production si beaucoup de données. console.table(data); // Appelle la fonction d’affichage HTML dans le div cible ; bonne pratique : séparer récupération des données et rendu visuel ; limite : le rendu HTML reste simple. this.renderResults(data, targetId, specialFields); // Affiche dans la console le nombre de résultats réellement retournés dans cette page ; bonne pratique : utile pour contrôler le volume affiché ; limite : RowCount n’est pas forcément le total global indexé. console.log("RowCount :", result.PrimaryQueryResult?.RelevantResults?.RowCount); // Affiche dans la console le nombre total estimé de résultats ; bonne pratique : utile pour savoir s’il faut paginer ; limite : TotalRows peut être une estimation selon SharePoint Search. console.log("TotalRows :", result.PrimaryQueryResult?.RelevantResults?.TotalRows); // Retourne les données transformées pour permettre une réutilisation externe ; bonne pratique : rend la fonction exploitable par un autre traitement ; limite : l’appelant doit attendre la Promise. return data; } // Capture les erreurs générées pendant l’appel ou le traitement ; bonne pratique : éviter une erreur JavaScript non gérée ; limite : ne distingue pas toujours les erreurs réseau, droits ou syntaxe KQL. catch (error) { // Affiche l’erreur complète dans la console ; bonne pratique : indispensable pour diagnostiquer ; limite : la console n’est pas visible par un utilisateur final. console.error("Erreur lors de la recherche SharePoint :", error); // Récupère le conteneur HTML cible ; bonne pratique : ne pas supposer que l’élément existe ; limite : si l’id est incorrect, aucun rendu ne sera possible. const container = document.getElementById(targetId); // Vérifie que le conteneur existe avant d’écrire dedans ; bonne pratique : évite une erreur JavaScript ; limite : l’utilisateur ne verra rien si l’id est faux. if (container) { // Affiche un message d’erreur dans le div cible ; bonne pratique : informer l’utilisateur final ; limite : attention à ne pas afficher d’informations sensibles en production. container.innerHTML = `