Backoffice - personnalisations
Le template personnalise le backoffice Umbraco sur quatre axes : login dev rapide, styles (login + indication d'environnement), 2FA, et un dashboard "Système".
Login dev rapide (Controllers/DevLoginController.cs)
GET /l, DEBUG uniquement (voir extensibility.md). Auto-connecte le premier utilisateur dont l'email finit par @spektrummedia.com. Double garde (#if DEBUG + IsDevelopment()) a ne jamais retirer. Evite de saisir des identifiants en developpement local.
App_Plugins
Tous declares via umbraco-package.json (manifests Umbraco 17, type backofficeEntryPoint).
CustomBackofficeLoginStyles
Personnalise l'ecran de login backoffice. backoffice.js (entry point) injecte backoffice-login.css. L'image de fond est servie depuis wwwroot/images/custom-backoffice-login/bg.jpg (referencee par Umbraco:CMS:Content:LoginBackgroundImage dans appsettings.json).
CustomEnvironmentBackofficeStyles
Affiche une indication visuelle de l'environnement courant dans le backoffice (pour ne pas confondre local / staging / prod). backoffice.js (onInit) injecte backoffice-env.css ; des variantes backoffice-env.master.css et backoffice-env.staging.css existent par environnement.
TwoFactorProviders
Manifeste (type mfaLoginProvider, alias UmbracoUserAppAuthenticator, label "Google Authenticator") qui declare cote UI le provider 2FA implemente cote serveur.
ButtonPicker
Property editor UI custom (type propertyEditorUi, alias Spektrum.PropertyEditorUi.ButtonPicker, schema Umbraco.TextBox) : groupe de boutons single-select stockant une classe CSS. button-picker.element.js rend les boutons (uui-button-group) ; button-picker-config.element.js (Spektrum.PropertyEditorUi.ButtonPickerConfig) est l'editeur de config du DataType (repeater libelle / classe). 100 % frontend. Detail d'usage dans modules/content-model.md.
SystemDashboard
Dashboard "Système" (onglet dans la section Contenu du backoffice). Manifeste umbraco-package.json (type dashboard, alias Web.Dashboard.System, label "Système", pathname systeme, condition Umb.Condition.SectionAlias = Umb.Section.Content). dashboard.js est un custom element Lit (web-system-dashboard) qui affiche trois boites : Version & build (version source, version Umbraco, environnement, hostname), Support et À propos. Il appelle l'endpoint backoffice GET /umbraco/management/api/v1/system-dashboard/info (Controllers/DashboardInfoController.cs, policy BackOfficeAccess), servi par Services/DashboardInfoService.cs (IDashboardInfoService, enregistre en singleton dans SiteComposer).
La version source est derivee du nombre de commits git (1.{minor}.{build}.{revision}), lue uniquement dans les variables d'env GIT_COMMIT_COUNT|SHA|DATE (injectees au build Docker, voir architecture/build-deploy.md). Absentes (dev local, ou build sans --build-arg) -> fallback 1.0.0.dev. Les coordonnees Support / À propos (email, telephone, adresse Spektrum) sont a personnaliser par projet.
Menu & pied de page editables (composition siteSettings)
La navigation (mega-menu, dropdowns) et le pied de page (nav, reseaux sociaux, liens legaux, contact, newsletter) sont edites sur le noeud racine du site (page d'accueil), pas dans du code. La composition siteSettings y ajoute deux onglets : Navigation (mainMenu, un constructeur de menu en Block List) et Pied de page (footerMenu, liens legaux, reseaux, champs contact/newsletter). Chaque item de menu = un bloc menuItem (label + lien optionnel + sous-liens menuLink) ; un sous-lien avec un En-tete de colonne (columnHeading) fait passer le dropdown en mega-menu multi-colonnes ; le champ Style (promo/all) applique une mise en avant. Modele/structure detailles : modules/content-model.md (section Navigation & pied de page). Pas de deploiement necessaire cote editeur — publier la page d'accueil suffit.
2FA - Google Authenticator (Services/UmbracoUserAppAuthenticator.cs)
ITwoFactorProvider (nom constant UmbracoUserAppAuthenticator), enregistre dans SiteComposer via BackOfficeIdentityBuilder.AddTwoFactorProvider<...>. Utilise le package GoogleAuthenticator :
GetSetupDataAsync: genere le QR-code de setup (TwoFactorAuthInfoModel) ; le nom d'application est en dur ("My application name") - a personnaliser par projet.ValidateTwoFactorPIN/ValidateTwoFactorSetup: valident le code TOTP.
Securite backoffice (appsettings.json)
- Politique mot de passe : >= 15 caracteres, majuscule + minuscule + chiffre + caractere non-alphanumerique.
AllowConcurrentLogins=false.DefaultUILanguage=fr-FR,TimeOut=01:00:00.
Revue "experience editeur" - la checklist qui manquait
Ce depot a longtemps ete verifie uniquement par le rendu : le harnais de fidelite prouve que la page ressemble au live au pixel pres, et ne dit rien de ce que l'editeur voit quand il ouvre le backoffice. La passe QA du 2026-07-29 a trouve, sur un site par ailleurs pixel-exact :
- 6 libelles de blocs en syntaxe AngularJS morte depuis la v14 (affiches litteralement
{{title}}), - 44 libelles de Block Grid statiques, donc dix blocs identiques indistinguables dans la liste,
- 4 grilles de marque qui avaient derive (un bloc present ici, absent la, sans raison),
- 2 icones inventees, donc 2 cases vides dans le selecteur de blocs,
- 81 proprietes sur 179 sans aucune description,
- 82 caracteres interdits (regle 8) et une dizaine de libelles francais sans accents,
- un champ
authorque l'editeur pouvait remplir et qu'aucune vue ne lisait.
Aucun de ces defauts n'est visible depuis le site. Tous le sont a la seconde ou un client ouvre l'editeur. D'ou cette checklist, a passer avant de dire qu'une fonctionnalite est finie.
Les portes automatiques (retour vide = conforme)
# 1. Libelles de blocs : plus aucune syntaxe AngularJS
grep -rn '{{' src/Web/uSync/
# 2. Icones : toutes existent dans le registre de la version installee
# (commande complete dans modules/content-model.md)
# 3. Regle 8 + accents, sur NOTRE texte uniquement
# (le contenu repris du live - Seed/, Controllers/DevSeed* - est exempt)
LC_ALL=C.UTF-8 grep -rnIP '[^\x00-\x7FàâäçéèêëîïñôöùûüÿœæÀÂÄÇÉÈÊËÎÏÑÔÖÙÛÜŸŒÆ©®™°€£µ]' \
src/Web/uSync src/Web/Views src/Web/styles src/Web/scripts --exclude-dir=logs
# 4. Toute propriete a une description
python - <<'PY'
import io, os, re
root = "src/Web/uSync/v17/ContentTypes"
for fn in sorted(os.listdir(root)):
if not fn.endswith(".config"): continue
s = io.open(os.path.join(root, fn), encoding="utf-8-sig").read()
for m in re.finditer(r"<GenericProperty>(.*?)</GenericProperty>", s, re.S):
b = m.group(1)
d = re.search(r"<Description><!\[CDATA\[(.*?)\]\]></Description>", b, re.S)
if not d or not d.group(1).strip():
print("SANS DESCRIPTION:", fn, re.search(r"<Alias>(.*?)</Alias>", b).group(1))
PYCe qu'aucune commande ne remplace
A faire dans le backoffice, pour chaque marque :
- Ouvrir la racine, ajouter chaque bloc du selecteur : chacun doit avoir une icone visible et, une fois rempli, un apercu lisible dans la liste (pas juste son type).
- Verifier que les 4 groupes du selecteur sont ceux attendus et qu'aucun bloc ne traine hors groupe.
- Ouvrir un champ Rich Text : la barre d'outils doit proposer le bloc FAQ, et l'apercu doit afficher la typographie du site (c'est
rte.css, cf.../architecture/frontend-build.md). - Creer un auteur et une categorie dans la Bibliothèque, puis les choisir sur un article : le picker ne doit proposer que les entites de cette marque.
- Relire les descriptions d'un doctype comme si on decouvrait le site : chacune doit dire ou le champ apparait et ce qui se passe s'il reste vide.
La regle derriere tout ca
La geometrie n'est jamais suffisante. Une page peut etre 1:1 au pixel et rester inutilisable pour celui qui doit la remplir. Un champ qui n'est lu par aucune vue, un libelle qui ne previsualise rien, une icone absente : rien de tout cela ne bouge d'un seul pixel sur le site.

