Patterns - comment ajouter / modifier
Ou placer un nouveau service (avant meme la question du DI)
La solution a 3 projets, references dans un seul sens - Web -> Intranet.Core -> Intranet.Connectivity (jamais l'inverse, garanti par le compilateur). Le projet d'un nouveau type se choisit par sa dependance, pas par une notion de couche :
- le type parle a un systeme externe (annuaire, API tierce) ->
Intranet.Connectivity(src/Intranet.Connectivity/, ex.LdapDirectoryService,LdapFilterHelper). Ce projet n'a aucune dependance Umbraco, et doit le rester ; - logique intranet, sans dependre d'un modele ModelsBuilder genere ->
Intranet.Core(src/Intranet.Core/, ex.MemberProvisioningService,MemberGateMiddleware). Une regle pure sans dependance Umbraco va ici aussi si elle porte de la logique intranet et non un acces externe : c'est le cas deDirectoryRulesetDirectoryContact; - des qu'un type est type sur un modele ModelsBuilder genere (
IPageSettings,EventPage, ...) ou sur des vues/controllers ->Web(src/Web/), le seul projet qui referenceumbraco/Models/*.generated.cs.
L'absence de dependance Umbraco ne suffit donc pas a envoyer un type dans Connectivity : le critere est a qui il parle, pas ce qu'il importe.
Voir overview.md pour la structure de solution complete et la raison du decoupage (reduire la surface de conflit avec le template upstream).
Ajouter un enregistrement DI / un wire-up Umbraco
src/Web/SiteComposer.cs (IComposer.Compose(IUmbracoBuilder builder)) appartient au template : ne plus y ajouter d'enregistrement directement, chaque ligne ajoutee ici entre en conflit a chaque merge upstream. Compose se limite a builder.AddIntranet(); builder.AddIntranetWeb(); plus le bloc 2FA du template (UmbracoUserAppAuthenticator, qui reste dans SiteComposer car fourni par le template).
Les enregistrements propres au projet vont dans l'une de deux methodes d'extension sur IUmbracoBuilder, selon ou vit le type enregistre :
Intranet.Core.Extensions.IntranetBuilderExtensions.AddIntranet()(src/Intranet.Core/Extensions/IntranetBuilderExtensions.cs) : enregistrements dont l'alias et l'implementation vivent dansIntranet.CoreouIntranet.Connectivity.Web.Extensions.IntranetWebBuilderExtensions.AddIntranetWeb()(src/Web/Extensions/IntranetWebBuilderExtensions.cs) : enregistrements sur des types qui restent dansWeb(controllers, vues, content finders, handlers lies au rendu/back-office). Necessaire carIntranet.Core/Intranet.Connectivityne peuvent pas referencerWeb(sens des references strict :Web->Intranet.Core->Intranet.Connectivity).
Dans les deux cas : DI classique (builder.Services.AddScoped<IFoo, Foo>()), content finders, notification handlers, etc. via les API builder.*, en preservant l'ordre relatif des enregistrements (certains notification handlers Umbraco sont sensibles a leur ordre) et leur duree de vie exacte. Eviter de mettre la logique de composition dans Program.cs (reserve au pipeline HTTP et au bootstrap).
Ajouter un content finder
- Implementer
IContentFinder(resolution normale) ouIContentLastChanceFinder(fallback type 404). - Injecter les services Umbraco par constructeur (constructeur primaire, cf.
NotFoundContentFinder). - Enregistrer dans
AddIntranetWeb()(src/Web/Extensions/IntranetWebBuilderExtensions.cs,SetContentLastChanceFinder<T>()pour le last-chance).
Ajouter un controller sur une route technique
- Creer le controller dans
src/Web/Controllers/. - Decorer avec
[Route("...")]. - Si la route est un chemin racine reserve (ex.
~/error/,~/events/), l'ajouter aUmbraco:CMS:Global:ReservedPathsdansappsettings.json. - Choix de la classe de base : sur un chemin reserve, Umbraco ne route pas la requete comme du contenu, donc pas de
UmbracoRouteValuesdans leHttpContext. Heriter deRenderControllery jette alorsInvalidOperationException: No UmbracoRouteValues feature was found. Utiliser unControllerMVC simple (cf.EventsController,RobotsController) et lire le contenu viaIPublishedContentQueryAccessor(l'UmbracoContextreste etabli parUmbracoRequestMiddlewarepour toute requete). ReserverRenderControlleraux routes routees comme des pages de contenu (cf.SitemapControllersur~/sitemap) ; la, forcer le statut a 200 (new ContentResult { StatusCode = 200 }) car Umbraco pre-positionne un 404.
Ajouter une extension (helpers contenu / SEO)
Les extensions de src/Web/Extensions/ operent sur IPublishedContent et les interfaces ModelsBuilder. Pattern (cf. PageExtensions) : methode statique this IPublishedContent, test if (page is IXxxSettings settings), resolution par priorite (head title -> meta title -> nom de page). Ajouter la methode dans le fichier d'extension adequat (PageExtensions, DateTimeExtensions, MediaWithCropsExtensions, PublishedContentExtensions, UmbracoContextExtensions).
Ajouter / modifier un document type
Editer via le backoffice Umbraco puis laisser uSync exporter (ExportOnSave="Settings") les .config sous src/Web/uSync/v17/, OU ecrire le .config directement. Les modeles ModelsBuilder (umbraco/Models/*.generated.cs) se regenerent automatiquement (SourceCodeAuto). Voir ../modules/content-model.md.
Acceder au contenu type (vues, controllers)
Preferer les modeles generes ModelsBuilder et les types de package plutot que Model.Value<T>("alias") / content.GetProperty("alias") (chaines magiques). Une vue herite du doctype (@inherits UmbracoViewPage<ArticlePage>) et lit Model.ArticleDate ; enumerer avec Descendants<ArticlePage>(), Children<EventFolder>(), Ancestors<EventsPage>() ; tester un type avec content is PageFolder folder ou block.Content as DocumentsBlock plutot que ContentType.Alias == "..." ; centraliser la logique derivee dans une extension typee (ex. EventExtensions.Coords(this EventPage)). Les types de package s'utilisent pareil (ex. EventPage.Location -> Our.Umbraco.GMaps.Models.Map -> Address.Coordinates.Latitude/Longitude). Value<T>("alias") ne reste justifie que sur du contenu heterogene (recherche/indexation sur n'importe quel doctype, ex. SearchService) ou dans un helper generique parametre par un alias.
Ajouter un theme frontend
Creer src/Web/styles/themes/<nom>/main.scss (importer Bootstrap et les partials base/). Vite globbe automatiquement les themes/*/main.scss et genere wwwroot/css/<nom>/main.css. Eventuellement un _bootstrap_variables.scss par theme pour surcharger les tokens. Voir ../architecture/frontend-build.md.
Ajouter un module JS
Creer src/Web/scripts/modules/<nom>.js exportant un objet avec init(), et l'importer en lazy depuis scripts/index.js (await import("./modules/<nom>")). Respecter ESLint (prefer-const, no-var, eqeqeq, prefer-template, ...).
Ajouter un outil marketing / analytics
Procedure dediee (consentement obligatoire) dans ../modules/cookies-marketing.md.
Ajouter un test
Reproduire le chemin de la source : src/Web/Services/Foo.cs -> tests/Web/Services/FooTests.cs. Heriter de TestBase (Tests.Common). xUnit ([Fact]/[Theory]) + Moq.

