Skip to content

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 de DirectoryRules et DirectoryContact ;
  • 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 reference umbraco/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 dans Intranet.Core ou Intranet.Connectivity.
  • Web.Extensions.IntranetWebBuilderExtensions.AddIntranetWeb() (src/Web/Extensions/IntranetWebBuilderExtensions.cs) : enregistrements sur des types qui restent dans Web (controllers, vues, content finders, handlers lies au rendu/back-office). Necessaire car Intranet.Core/Intranet.Connectivity ne peuvent pas referencer Web (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

  1. Implementer IContentFinder (resolution normale) ou IContentLastChanceFinder (fallback type 404).
  2. Injecter les services Umbraco par constructeur (constructeur primaire, cf. NotFoundContentFinder).
  3. Enregistrer dans AddIntranetWeb() (src/Web/Extensions/IntranetWebBuilderExtensions.cs, SetContentLastChanceFinder<T>() pour le last-chance).

Ajouter un controller sur une route technique

  1. Creer le controller dans src/Web/Controllers/.
  2. Decorer avec [Route("...")].
  3. Si la route est un chemin racine reserve (ex. ~/error/, ~/events/), l'ajouter a Umbraco:CMS:Global:ReservedPaths dans appsettings.json.
  4. Choix de la classe de base : sur un chemin reserve, Umbraco ne route pas la requete comme du contenu, donc pas de UmbracoRouteValues dans le HttpContext. Heriter de RenderController y jette alors InvalidOperationException: No UmbracoRouteValues feature was found. Utiliser un Controller MVC simple (cf. EventsController, RobotsController) et lire le contenu via IPublishedContentQueryAccessor (l'UmbracoContext reste etabli par UmbracoRequestMiddleware pour toute requete). Reserver RenderController aux routes routees comme des pages de contenu (cf. SitemapController sur ~/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.

Contributors

No contributors

Changelog

No recent changes