Se rendre au contenu

Intégrer Odoo à un site existant

3 février 2025 par
LCSX Tech, Laurent Cossiaux
| Aucun commentaire pour l'instant

Si vous souhaitez intégrer à un site existant une ou plusieurs page Odoo, d'une manière et parfaitement intégrée, il y a différentes façon :

1. Afficher le site Odoo dans un cadre (iframe) d'une page existante

C'est la façon le plus simple.
L'idéal c'est d'enlever l'entête (header) et le pied de page (footer) de la page à intégrer.

C'est possible de les enlever depuis l'éditeur du site d'Odoo.

Cependant, cela veut dire que si la page est affiché en dehors du cadre du site existant, pour raison ou une autre (exemple : la page c'est fait indexée par Google), nous allons afficher un site sans entête, ni pied de page, ce qui n'est pas l'idéal.

Une façon simple de gérer cela est de créé une entête et un pieds de page respectant la charte graphique du site existant et s'approchant de l'existant. 

Puis d'ajouter un script javascript dans la page qui détecte si l'on est dans un iframe (cadre) ou pas. Si nous sommes dans un iframe, le script cache le header et le pied de page.

<script>

function inIframe() {

    var w = window, b;  

    try {

        b = w.self !== w.top;

    } catch(e) {

        b = true;

    }

    return b;

}


// detect if the script is running in an iframe, if so, hide the odoo website header

document.addEventListener('DOMContentLoaded', function() {

    if (inIframe()) {

        var iframe = window.frameElement;

        if (iframe && iframe.classList.contains('o_iframe')) {

            return; // Do not hide header and footer if iframe has 'o_iframe' class (meaning it is an Odoo iframe)

        }

        var header = document.querySelector('header');

        var footer = document.querySelector('footer');

        if (header) {

            header.style.display = 'none';

        }

        if (footer) {

            footer.style.display = 'none';

        }

    }

});


</script>


Avantages:

  • l'url reste sur le même domaine/sous domaine que le reste du site

Inconvénient:

  • Limité aux pages qui ne nécessite pas d'être logué dans Odoo
    • En effet, il existe une protection et les pages nécessitant d'être connecté afficherons un message d'erreur
    • Il est possible d'outre passer cette limitation sur les versions hébergées mais cela nécessite une modifications du code Odoo.


2. Rediriger simplement vers la page Odoo


Plutôt simple, cependant si l'on souhaite une intégration fluide, il faut reprendre toute l'entête et le pied de page dans Odoo. Si des entêtes simple cela peut être réalisable, cependant avec les entêtes construites avec Wordpress par exemple, dupliquer dans Odoo s'avère être plus complexe et très chronophage. 

Une solution à cela, sera de juste prendre l'entête du site existant pour la mettre dans Odoo. 

Encore une fois avec l'entête d'un site Wordpress, cela est complexe car il y a pleins de dépendances. 


Voici donc script à coller dans le code html de votre page Odoo:

<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
        <script>

          $(document).ready(function(){

            // URL de base de la page externe

            var externalBaseUrl = "https://www.lcsx.tech/";

            var localStorageKeyPage = "externalHTML";

            // Fonction de chargement et mise en cache d'une feuille de style (pour 2 jours)

            function loadCachedStylesheet(url) {

              // Exclure les URLs contenant "recaptcha"

              if (url.toLowerCase().indexOf("recaptcha") !== -1) {

                console.log("Exclusion CSS recaptcha : " + url);

                return;

              }

              var key = "cssCache:" + url;

              var now = Date.now();

              var expiry = 2 * 24 * 60 * 60 * 1000; // 2 jours en millisecondes

              var cached = localStorage.getItem(key);

              if (cached) {

                try {

                  var data = JSON.parse(cached);

                  if (now - data.timestamp < expiry && data.css) {

                    var styleEl = document.createElement("style");

                    styleEl.textContent = data.css;

                    document.head.appendChild(styleEl);

                    console.log("Chargement depuis le cache : " + url);

                    return;

                  }

                } catch(e) {

                  console.error("Erreur lors du parsing du cache pour " + url, e);

                }

              }

              // Si pas en cache ou expiré, on le récupère via AJAX

              $.ajax({

                url: url,

                dataType: "text"

              }).done(function(cssContent) {

                try {

                  localStorage.setItem(key, JSON.stringify({ timestamp: now, css: cssContent }));

                } catch(e) {

                  console.error("Erreur lors du stockage dans localStorage pour " + url, e);

                }

                var styleEl = document.createElement("style");

                styleEl.textContent = cssContent;

                document.head.appendChild(styleEl);

                console.log("CSS chargé et mis en cache : " + url);

              }).fail(function() {

                console.error("Erreur lors du chargement du CSS : " + url);

              });

            }

            // Fonction qui traite le document HTML externe

            function processData(data) {

              // Utilisation de DOMParser pour obtenir un document complet

              var parser = new DOMParser();

              var doc = parser.parseFromString(data, "text/html");

              var $doc = $(doc);

              /* ============================================================

               * 1. Traitement des ressources du <head> externe

               * ============================================================ */

              // 1a. Feuilles de style (<link rel="stylesheet">) du head

              $doc.find("head link[rel='stylesheet']").each(function(){

                var href = $(this).attr("href");

                if (href) {

                  try {

                    href = new URL(href, externalBaseUrl).href;

                  } catch(e) {

                    console.error("Erreur de conversion d'URL pour <link> dans head :", href, e);

                  }

                  console.log("Traitement CSS (head) : " + href);

                  loadCachedStylesheet(href);

                }

              });

              // 1b. Balises <style> du head

              $doc.find("head style").each(function(){

                var cssText = $(this).html();

                console.log("Insertion d'un <style> du head (premiers 30 caractères) : " + cssText.substring(0, 30) + "...");

                var styleEl = document.createElement("style");

                styleEl.textContent = cssText;

                document.head.appendChild(styleEl);

              });

              // 1c. Scripts du head

              $doc.find("head script").each(function(){

                var scriptElem = this;

                if (scriptElem.src) {

                  var src = scriptElem.src;

                  if (src.toLowerCase().indexOf("recaptcha") !== -1) {

                    console.log("Exclusion script externe (head) recaptcha : " + src);

                    return true;

                  }

                  try {

                    src = new URL(src, externalBaseUrl).href;

                  } catch(e) {

                    console.error("Erreur de conversion d'URL pour <script> dans head :", src, e);

                  }

                  console.log("Chargement script externe (head) : " + src);

                  var newScript = document.createElement("script");

                  newScript.src = src;

                  document.head.appendChild(newScript);

                } else {

                  console.log("Exécution script inline (head)");

                  $.globalEval(scriptElem.text || scriptElem.textContent || scriptElem.innerHTML || "");

                }

              });

              /* ============================================================

               * 2. Remplacement du premier <header> de la page locale par le premier <header> externe

               * ============================================================ */

              var $externalHeader = $doc.find("header").first();

              if ($externalHeader.length) {

                var $headerClone = $externalHeader.clone();

                // Retirer les scripts pour éviter leur exécution multiple

                $headerClone.find("script").remove();

                $("header").first().html($headerClone.html());

                // Exécuter les scripts du header externe

                $externalHeader.find("script").each(function(){

                  var scriptElem = this;

                  if (scriptElem.src) {

                    var src = scriptElem.src;

                    if (src.toLowerCase().indexOf("recaptcha") !== -1) {

                      console.log("Exclusion script externe (header) recaptcha : " + src);

                      return true;

                    }

                    try {

                      src = new URL(src, externalBaseUrl).href;

                    } catch(e) {

                      console.error("Erreur de conversion d'URL pour <script> dans header :", src, e);

                    }

                    console.log("Chargement script externe (header) : " + src);

                    var newScript = document.createElement("script");

                    newScript.src = src;

                    document.head.appendChild(newScript);

                  } else {

                    console.log("Exécution script inline (header)");

                    $.globalEval(scriptElem.text || scriptElem.textContent || scriptElem.innerHTML || "");

                  }

                });

              } else {

                console.error("Aucun <header> trouvé dans le document externe.");

              }

              /* ============================================================

               * 3. Traitement des ressources du <body> externe (hors <header>)

               * ============================================================ */

              var $externalBody = $doc.find("body");

              if ($externalBody.length) {

                // 3a. Feuilles de style (<link rel="stylesheet">) dans le body hors header

                $externalBody.find("link[rel='stylesheet']").filter(function(){

                  return $(this).closest("header").length === 0;

                }).each(function(){

                  var href = $(this).attr("href");

                  if (href) {

                    try {

                      href = new URL(href, externalBaseUrl).href;

                    } catch(e) {

                      console.error("Erreur de conversion d'URL pour <link> dans body :", href, e);

                    }

                    console.log("Traitement CSS (body) : " + href);

                    loadCachedStylesheet(href);

                  }

                });

                // 3b. Balises <style> dans le body hors header

                $externalBody.find("style").filter(function(){

                  return $(this).closest("header").length === 0;

                }).each(function(){

                  var cssText = $(this).html();

                  console.log("Insertion d'un <style> du body (premiers 30 caractères) : " + cssText.substring(0, 30) + "...");

                  var styleEl = document.createElement("style");

                  styleEl.textContent = cssText;

                  document.head.appendChild(styleEl);

                });

                // 3c. Scripts dans le body hors header

                $externalBody.find("script").filter(function(){

                  return $(this).closest("header").length === 0;

                }).each(function(){

                  var scriptElem = this;

                  if (scriptElem.src) {

                    var src = scriptElem.src;

                    if (src.toLowerCase().indexOf("recaptcha") !== -1) {

                      console.log("Exclusion script externe (body) recaptcha : " + src);

                      return true;

                    }

                    try {

                      src = new URL(src, externalBaseUrl).href;

                    } catch(e) {

                      console.error("Erreur de conversion d'URL pour <script> dans body :", src, e);

                    }

                    console.log("Chargement script externe (body) : " + src);

                    var newScript = document.createElement("script");

                    newScript.src = src;

                    document.head.appendChild(newScript);

                  } else {

                    console.log("Exécution script inline (body)");

                    $.globalEval(scriptElem.text || scriptElem.textContent || scriptElem.innerHTML || "");

                  }

                });

              } else {

                console.error("Aucun <body> trouvé dans le document externe.");

              }

              /* ============================================================

               * 4. Modification de la couleur d'arrière-plan d'un div ciblé

               * ============================================================ */

              $(".elementor-element-5bb52cd").css("background-color", "#FFF1B2");

              // Une fois que tout est traité, on affiche la page

              document.body.style.visibility = 'visible';

            }

            // Vérifier si la page externe est en cache dans le localStorage (pour l'ensemble du HTML)

            var cachedHTML = localStorage.getItem(localStorageKeyPage);

            if (cachedHTML) {

              console.log("Chargement du HTML externe depuis le cache localStorage");

              processData(cachedHTML);

            } else {

              // Sinon, on charge le HTML externe et on le met en cache

              $.ajax({

                url: externalBaseUrl,

                dataType: "html",

                cache: true

              }).done(function(data){

                try {

                  localStorage.setItem(localStorageKeyPage, data);

                } catch(e) {

                  console.error("Erreur lors du stockage du HTML externe dans localStorage", e);

                }

                processData(data);

              }).fail(function(jqXHR, textStatus, errorThrown){

                console.error("Erreur lors du chargement de la page externe :", textStatus, errorThrown);

                document.body.style.visibility = 'visible';

              });

            }

          });

        </script>


Le script récupère le code html de l'entête du site source pour le mettre sur la page de destination. Mais il faut aussi les choses suivantes:

  • Récupération des scripts et feuilles de style dans le head de la page html
  • Récupération des scripts et feuilles de style dans le body de la page html
  • Mise en cache pour un chargement rapide


Note : Vu que l'on récupéré des scripts et des feuilles de style qui vont s'appliquer sur Odoo. Il est possible que cela casse quelques mise en page d'Odoo. A bien tester donc.

On peut éviter cet inconvénient en passant par le shadow DOM. 
Je ferai un article dessus prochainement.



LCSX Tech, Laurent Cossiaux 3 février 2025
Partager cet article
Étiquettes
Se connecter pour laisser un commentaire.
Odoo Owl : Passer des infos de session au composant