# DfCustomCodeManager — Guide complet

> DfCustomCodeManager apporte une réponse simple à un problème universel des boutiques Shopware : où mettre, comment versionner et comment sécuriser le CSS et le JavaScript custom qu'on finit toujours par…

- Page: <https://www.datafirefly.com/documentation/df-custom-code-manager/>
- Langue: fr
- Mis à jour le: 2026-06-29
- Autres langues: [en](https://www.datafirefly.com/en/documentation/df-custom-code-manager/index.md), [es](https://www.datafirefly.com/es/documentation/df-custom-code-manager/index.md), [de](https://www.datafirefly.com/de/documentation/df-custom-code-manager/index.md), [it](https://www.datafirefly.com/it/documentation/df-custom-code-manager/index.md), [pl](https://www.datafirefly.com/pl/documentation/df-custom-code-manager/index.md), [nl](https://www.datafirefly.com/nl/documentation/df-custom-code-manager/index.md), [pt](https://www.datafirefly.com/pt/documentation/df-custom-code-manager/index.md)
- Index: <https://www.datafirefly.com/documentation/llms.txt>

DfCustomCodeManager apporte une réponse simple à un problème universel des boutiques Shopware : **où mettre, comment versionner et comment sécuriser le CSS et le JavaScript custom** qu'on finit toujours par ajouter à un thème ? Le plugin propose un éditeur de code intégré à l'administration, un système de **conteneurs** regroupant des **snippets SCSS ou JavaScript**, et surtout une **injection directe dans la compilation du thème** via les événements natifs de Shopware. Conséquence : aucun fichier servi en plus, aucune requête HTTP additionnelle pour le visiteur, et vos snippets SCSS héritent automatiquement des variables et mixins du thème actif. Ce guide couvre l'installation, la configuration, la création de conteneurs et snippets, la compilation du thème, l'historique de versions, le safe mode, la bibliothèque de presets, l'import/export et le dépannage.

## Installation

1. Téléchargez l'archive `DfCustomCodeManager-1.0.0.zip` depuis votre espace DataFirefly.
2. Installez-la via **Administration → Extensions → Mes extensions → Téléverser une extension**, ou décompressez le dossier `DfCustomCodeManager` dans `custom/plugins/`.
3. Lancez l'installation et l'activation : ``` bin/console plugin:refresh bin/console plugin:install --activate DfCustomCodeManager bin/console cache:clear ```
4. À l'installation, le plugin crée ses 4 tables (`df_ccm_container`, `df_ccm_container_sales_channel`, `df_ccm_snippet`, `df_ccm_snippet_version`) et enregistre ses subscribers aux événements de compilation du thème.
5. Recompilez le thème pour qu'il prenne en compte le plugin : ``` bin/console theme:compile ```

Compatible Shopware 6.6.x et 6.7.x sur un même code (composer `^6.6.0 || ^6.7.0`). Le module d'administration est **précompilé**, **aucun build n'est nécessaire**. PHP 8.2+ requis. La compilation SCSS s'appuie sur `scssphp/scssphp`, déjà fourni par `shopware/storefront`. Aucune dépendance Composer supplémentaire.

## Où trouver le plugin dans l'administration

Après activation, une entrée **Custom Code Manager** apparaît dans le menu **Catalogues** de l'administration (icône orange, position 100). C'est l'écran central : liste des conteneurs, recherche, filtres, et les actions globales **Importer**, **Exporter**, **Presets** et **Compiler le thème**. Toute la configuration de bas niveau (safe mode, minify, bannière) se fait depuis **Extensions → Mes extensions → DfCustomCodeManager → ⋯ → Configurer**.

Si l'entrée n'apparaît pas après une mise à jour, lancez `bin/console assets:install && bin/console cache:clear` puis rechargez l'administration avec un rafraîchissement forcé (Ctrl+Shift+R).

## Configuration du plugin

La carte de configuration plugin Shopware expose trois interrupteurs simples mais essentiels :

- **Safe mode** (`DfCustomCodeManager.config.safeMode`) : désactivé par défaut. Quand il est activé, **tous** les conteneurs sont ignorés à la prochaine compilation, comme s'ils étaient tous inactifs. C'est le filet de sécurité décrit plus bas.
- **Minify JS** (`DfCustomCodeManager.config.minifyJs`) : minification basique du JavaScript injecté. Utile en production, à laisser désactivé en développement pour pouvoir lire les snippets dans le bundle compilé.
- **Ajouter une bannière** (`DfCustomCodeManager.config.addBanner`) : ajoute un commentaire `/* DataFirefly Custom Code Manager */` au début du code injecté. Pratique pour repérer rapidement vos snippets dans le bundle compilé.

## Le modèle mental : conteneurs et snippets

Le plugin s'organise sur **deux niveaux** :

- Un **conteneur** est un regroupement logique : _Promo été 2026_, _GTM analytics_, _Dark mode opt-in_, _A/B test bouton CTA_… Chaque conteneur a un nom, une description, une priorité, un toggle actif/inactif et une portée par canal de vente.
- Un **snippet** est une unité de code à l'intérieur d'un conteneur. Type SCSS ou JavaScript, nom, code, priorité, toggle actif/inactif, notes Markdown, et un toggle pour activer ou non l'injection des variables du thème.

Un conteneur peut contenir **plusieurs snippets de types mélangés**. C'est utile pour regrouper logiquement les snippets qui vont ensemble : par exemple un conteneur _Dark mode opt-in_ avec un snippet SCSS pour les styles et un snippet JavaScript pour le toggle de classe sur `html`.

## Créer son premier conteneur

1. Depuis **Catalogues → Custom Code Manager**, cliquez sur **Nouveau conteneur** en haut à droite.
2. Donnez-lui un nom évocateur (ex. _Promo été 2026 — sticky header & badge_).
3. Réglez la **priorité** (laissez 0 par défaut, montez la valeur si vous voulez que ce conteneur soit injecté plus tard dans le bundle et donc surcharge d'autres styles).
4. (Optionnel) Activez **Limiter à des canaux de vente** et sélectionnez un ou plusieurs canaux. Si l'option est désactivée, le conteneur s'applique à **tous** les canaux.
5. Renseignez une description (notes internes, contexte, ticket associé) — elle ne sera jamais injectée dans le bundle.
6. Enregistrez. Vous êtes maintenant prêt à ajouter des snippets.

## Ajouter un snippet SCSS ou JavaScript

Dans la carte **Extraits de code** de la page conteneur, deux boutons : **Ajouter du SCSS** et **Ajouter du JavaScript**. Chaque snippet ajouté apparaît sous forme de carte avec :

- Un badge **SCSS** (info) ou **JS** (warning).
- Un champ nom (visible en édition uniquement).
- Un toggle actif/inactif.
- Un bouton **Valider la syntaxe** (✓).
- Un bouton **Historique** (horloge) — disponible dès le premier enregistrement.
- Un bouton **Dupliquer**.
- Un bouton **Supprimer**.
- Un **éditeur de code** avec coloration syntaxique adaptée au type.
- Une zone **Notes** Markdown pour documenter ce que fait le snippet.

L'éditeur de code utilise nativement le composant `mt-code-editor` introduit dans Shopware 6.7 (basé sur Meteor). Sur Shopware 6.6, le plugin bascule automatiquement sur `sw-code-editor` (l'ancien composant basé sur Ace). En dernier recours (composant indisponible), un `textarea` en police monospace prend le relais — sans coloration syntaxique mais parfaitement fonctionnel.

## Compiler le thème

Un snippet enregistré n'est **pas encore visible** sur le storefront tant que le thème n'a pas été recompilé. Deux façons de le faire :

- Depuis l'administration, bouton **Compiler le thème** (en haut à droite de la liste, ou dans la page conteneur). Une notification confirme la fin de l'opération.
- Depuis la ligne de commande : ``` bin/console theme:compile ```

À la compilation, le plugin écoute trois événements Shopware et y injecte vos snippets :

- `ThemeCompilerEnrichScssVariablesEvent` — capture la map des variables SCSS du thème actif. C'est ce qui permet à vos snippets SCSS d'utiliser `$sw-color-brand-primary`, `$font-family-base`, etc.
- `ThemeCompilerConcatenatedStylesEvent` — concatène votre SCSS compilé en fin de feuille de styles principale.
- `ThemeCompilerConcatenatedScriptsEvent` — concatène votre JavaScript en fin de bundle scripts principal.

Résultat : **zéro requête HTTP supplémentaire** servie au visiteur, votre code passe par toute la chaîne d'optimisation Shopware (concaténation, minification, fingerprinting de cache).

## Héritage des variables et mixins du thème

Par défaut, chaque snippet SCSS bénéficie d'un **préambule automatique** contenant toutes les variables et tous les mixins exposés par le thème actif et ses plugins de thème. Vous pouvez donc écrire dans un snippet :

```
.header {
    background: $sw-color-brand-primary;
    font-family: $font-family-base;
    transition: $transition-base;
}
```

…sans rien importer manuellement, exactement comme dans un fichier SCSS du thème. Si pour une raison particulière (conflit de variable, snippet auto-contenu) vous voulez désactiver cet héritage sur un snippet précis, décochez le toggle **Variables du thème disponibles** dans le footer de la carte snippet.

## Portée multi-canal

Sur la carte **Portée & canaux** du conteneur, activez **Limiter à des canaux de vente** et sélectionnez les canaux concernés. Le conteneur ne sera alors injecté que dans les compilations de thème pour ces canaux. Très pratique pour :

- Une bannière promo réservée à une boutique de marque secondaire.
- Un script de tracking spécifique à un marché.
- Un test A/B sur un seul canal pour mesurer l'impact.

## Priorité de chargement

La priorité est un entier (par défaut 0) qui contrôle l'**ordre d'injection** dans le bundle compilé : plus la valeur est élevée, plus le code est injecté tardivement, et donc plus il peut surcharger ce qui le précède. La priorité existe à **deux niveaux** :

- **Au niveau conteneur** : ordre entre conteneurs.
- **Au niveau snippet** : ordre entre snippets _à l'intérieur_ d'un même conteneur.

L'ordre final est : `(priorité conteneur, priorité snippet)` en ordre croissant. Conseil simple : laissez tout à 0 par défaut, n'utilisez la priorité que si vous avez explicitement besoin de surcharger quelque chose.

## Historique des versions

À chaque enregistrement d'un snippet, le plugin crée automatiquement une **version** dans la table `df_ccm_snippet_version`. Les **5 dernières versions** par snippet sont conservées (la plus ancienne est supprimée quand le seuil est atteint). Pour consulter et restaurer une version :

1. Sur la carte snippet, cliquez sur l'icône **Historique** (horloge).
2. Une modale s'ouvre avec la liste des versions, date, auteur, commentaire et aperçu.
3. Cliquez sur **Restaurer** à droite de la version souhaitée — le code du snippet est immédiatement remplacé par celui de la version. Une nouvelle version est créée pour acter la restauration.
4. Enregistrez le conteneur et recompilez pour voir l'effet.

Pour des historiques plus longs, le journal complet reste dans la table `df_ccm_snippet_version` (les vieilles versions sont seulement masquées de la modale). Un développeur peut très facilement étendre `VersionTracker::MAX_VERSIONS_PER_SNIPPET` si nécessaire.

## Safe mode : le filet de sécurité d'urgence

Le safe mode est un toggle global dans la configuration plugin. Activé, il fait que **tous les conteneurs sont ignorés à la prochaine compilation**, sans modifier la moindre donnée. Utilisation typique :

1. Un snippet mal testé casse quelque chose en production à 22h.
2. Allez dans **Extensions → Mes extensions → DfCustomCodeManager → ⋯ → Configurer**.
3. Activez le toggle **Safe mode** et enregistrez.
4. Recompilez le thème : `bin/console theme:compile`.
5. Le storefront retrouve son état natif (sans aucun snippet injecté). Vous pouvez maintenant identifier et corriger le snippet fautif au calme.
6. Une fois corrigé, désactivez le safe mode et recompilez à nouveau.

Le safe mode est un outil d'urgence, pas un mode d'exploitation. Une fois l'incident résolu, pensez à le désactiver, sinon aucun de vos conteneurs ne sera injecté.

## Bibliothèque de presets

Le bouton **Presets** (depuis la liste des conteneurs) ouvre une bibliothèque de 8 conteneurs prêts à installer en un clic :

- **Sticky header** — header qui se transforme au scroll (classe `is-sticky` ajoutée au-delà d'un seuil).
- **Back to top** — bouton retour en haut, visible à partir d'un certain scroll.
- **Cookie banner skin** — re-skinning du bandeau cookies natif pour l'aligner sur votre charte.
- **Product badge — Nouveau** — badge "Nouveau" sur les produits créés récemment.
- **Free shipping bar** — barre de progression livraison gratuite en haut de page.
- **Rounded buttons** — boutons arrondis sur toute la boutique.
- **GTM DOM-ready event** — déclenche un event personnalisé une fois le DOM prêt, pour amorcer vos data layers.
- **Fade-in on scroll** — apparition progressive des éléments au scroll.

Cliquer sur **Installer** crée le conteneur et ses snippets dans la base. Il ne reste plus qu'à recompiler le thème pour les voir en ligne. Les conteneurs créés à partir d'un preset sont des conteneurs comme les autres : vous pouvez les éditer, les enrichir, les désactiver, les supprimer.

## Import / Export JSON

Pour migrer des snippets entre environnements (dev, staging, production) ou entre boutiques :

1. **Export** — sélectionnez un ou plusieurs conteneurs dans la liste (cases à cocher), puis cliquez sur **Exporter**. Un fichier JSON est téléchargé, contenant tous les conteneurs sélectionnés avec leurs snippets, leur priorité, leurs notes — et le numéro de version du format (`EXPORT_VERSION = 1`) pour la rétrocompatibilité.
2. **Import** — sur l'environnement de destination, cliquez sur **Importer** et sélectionnez le fichier JSON. Les conteneurs et snippets sont recréés à l'identique.

L'import ne touche pas aux conteneurs existants (pas de fusion par nom) : il crée systématiquement de nouvelles entrées. Si vous voulez écraser un conteneur existant, supprimez-le manuellement avant d'importer.

## Validation syntaxique

Le bouton ✓ **Valider la syntaxe** sur chaque carte snippet déclenche une validation côté serveur sans rien enregistrer. Selon le type :

- **SCSS** — le plugin lance une **compilation à blanc** via `scssphp/scssphp` en injectant le préambule des variables du thème. Si la compilation passe, le snippet est valide. Sinon, les erreurs sont remontées dans une zone d'erreur sur la carte (numéro de ligne, message).
- **JavaScript** — le plugin nettoie le code des chaînes et commentaires, puis vérifie l'**équilibre des accolades** `{}`, **parenthèses** `()` et **crochets** `[]`. Ce check ne remplace pas un vrai parser ECMAScript, mais il attrape 90 % des erreurs de copier-coller (accolade oubliée, chaîne mal fermée). Pour aller plus loin, validez votre JavaScript dans un outil dédié avant collage.

## Architecture technique

Pour les développeurs qui veulent comprendre ou étendre le plugin :

- **Namespace PHP racine** : `DataFirefly` (sous-namespace : `CustomCodeManager`, classes sous `src/`).
- **Plugin class** : `DfCustomCodeManager` (étend `ShopwareCoreFrameworkPlugin`).
- **Préfixe SystemConfig** : `DfCustomCodeManager.config.*`.
- **Tables** : `df_ccm_container`, `df_ccm_container_sales_channel`, `df_ccm_snippet`, `df_ccm_snippet_version`.
- **Migrations** : `Migration1779580800CreateCcmContainerTable`, `Migration1779580801CreateCcmSnippetTable`, `Migration1779580802CreateCcmSnippetVersionTable`.
- **Services** : `CodeCompiler` (orchestration + cache), `SnippetExporter` (import/export JSON), `SnippetValidator` (validation syntaxique), `VersionTracker` (snapshot + restore + trim).
- **Subscribers** : `ThemeCompilerSubscriber` (les 3 events de compilation), `SnippetWrittenSubscriber` (snapshot auto sur écriture).
- **Routes API privées** sous `/api/_action/df-ccm/` (scope `api`) : `validate`, `export`, `import`, `restore-version`, `recompile`, `presets`.

Toutes les déclarations de services sont explicites dans `services.xml` (pas d'autowiring) pour rester aligné sur les conventions DataFirefly.

## Étendre le module d'administration

Le module d'administration est compilé et placé sous `Resources/public/administration/js/df-custom-code-manager.js`. Il est **précompilé dans le ZIP** et ne nécessite **aucun build local**. Pour l'étendre, dépliez les composants (`df-ccm-list`, `df-ccm-detail`, `df-ccm-snippet-card`, `df-ccm-code-field`, `df-ccm-preset-modal`, `df-ccm-version-modal`) et personnalisez via le système d'override standard Shopware (`Component.override`).

## FAQ et dépannage

**Mes snippets n'apparaissent pas sur le storefront.** Avez-vous recompilé le thème ? Tant que `theme:compile` n'a pas tourné après modification, rien ne change. Vérifiez aussi que le conteneur et le snippet sont bien actifs, que le canal de vente concerné est dans la portée (si la portée est restreinte), et que le safe mode n'est pas activé.

**Erreur de compilation SCSS dans la console après theme:compile.** Le plus souvent, c'est une variable de thème qui n'existe pas (faute de frappe) ou un snippet SCSS qui ouvre un bloc sans le refermer. Désactivez le snippet incriminé, recompilez pour confirmer, puis corrigez à tête reposée. La validation syntaxique sur la carte snippet attrape la plupart de ces cas avant enregistrement.

**Le menu Custom Code Manager n'apparaît pas dans l'admin.** Lancez `bin/console assets:install && bin/console cache:clear` puis rechargez l'administration avec Ctrl+Shift+R. Le module est dans le groupe **Catalogues**.

**L'éditeur de code affiche un textarea sans coloration.** C'est le fallback quand ni `mt-code-editor` (6.7) ni `sw-code-editor` (6.6) ne sont disponibles. Vérifiez votre version Shopware et que le module d'administration est bien chargé.

**Je veux désactiver temporairement tous les snippets sans rien supprimer.** C'est exactement le safe mode. Toggle dans la config plugin, recompilation, fini.

**Le bouton "Compiler le thème" tourne sans fin.** Vérifiez les logs Shopware. La compilation peut prendre du temps sur un gros thème ; en cas de blocage réel, la cause habituelle est un snippet SCSS qui boucle ou contient une variable inconnue. Activez le safe mode, recompilez, puis réactivez les snippets un par un pour identifier le coupable.

**Mon snippet JavaScript ne se déclenche pas.** N'oubliez pas que le code injecté est exécuté **dans le bundle global**, donc dans un contexte différent de celui des plugins Shopware classiques. Pour interagir avec les plugins JavaScript du storefront, écoutez plutôt `document.addEventListener('DOMContentLoaded', …)` ou les events globaux que le storefront émet.

**Que se passe-t-il à la désinstallation ?** Lors de `plugin:uninstall`, Shopware affiche la case standard **Conserver les données utilisateur**. Si elle est **décochée**, le plugin supprime ses 4 tables et toutes les données associées (conteneurs, snippets, versions, liaisons canal). Si elle est **cochée**, les tables restent en place et vous pourrez retrouver vos snippets si vous réinstallez le plugin plus tard.
