Modele de contenu
Tout est serialise via uSync sous src/Web/uSync/v17/. Les modeles C# correspondants sont generes par ModelsBuilder dans src/Web/umbraco/Models/*.generated.cs (ne pas editer). Le modele livre est volontairement minimal (gabarit) avec des exemples a adapter.
Document types (uSync/v17/ContentTypes/)
| Alias | Nom | Role |
|---|---|---|
homePage | Page d'accueil | Racine de site. Compose cookiesSettings, exampleSection, genericSection (+ autres compositions). Template homePage. Enfants autorises : freeContentPage, folder, searchPage, notFoundPage, internalServerErrorPage. |
searchPage | Page de recherche | Page de resultats de recherche (Examine). Compose pageSettings. Template SearchPage. Proprietes : contentStartNode (ContentPicker, restreint la recherche sous un noeud), excludeContent, excludeMedia (Truefalse). Rendu serveur : la vue appelle ISearchService.SearchWithPage(Model, q) (form GET ?q=), avec badge de type par resultat (Page/Document-PDF/Evenement/Article) et pastilles de filtre cote client. Les eventPage remontent dans les resultats (distingues comme « Evenement »). Porte depuis accm-website. Voir extensibility.md. |
freeContentPage | Page generique | Page de contenu routable. Compose pageSettings (SEO) + genericSection (BlockList hero au-dessus du BlockGrid blocks). Template FreeContentPage. Ajoutable sous homePage et sous elle-meme. Enfants : freeContentPage, folder. La vue reutilise le partial sections/genericSection.cshtml. Propriete optionnelle redirectTarget (MultiUrlPicker, onglet « Redirection », meme mecanisme que pageFolder) : si renseignee, redirige (302) vers une page, un media (PDF...) ou une URL externe (voir RedirectTargetHandler, extensibility.md) ; sinon page de contenu normale. |
folder | Dossier | Conteneur d'organisation de pages dans l'arbre (ex. « Chantiers » -> pages dedans). Aucun template -> non rendu ET auto-exclu du sitemap et de la recherche (le SitemapController et SearchService ignorent les noeuds sans template mais parcourent leurs enfants). Enfants : freeContentPage, folder. Propriete conventionnelle umbracoRedirect (ContentPicker) : si renseignee, Umbraco redirige (302) vers la page choisie ; sinon simple dossier. Ajoutable sous homePage et freeContentPage. |
pageSettings | Page Settings | Composition SEO/sitemap partagee par les pages (meta description, masquage sitemap...). |
notFoundPage | Page 404 | Page d'erreur 404 par site (resolue par NotFoundContentFinder). Compose pageSettings. Exclue du sitemap et de la recherche par type (elle a un template, donc pas couverte par la regle du template). |
internalServerErrorPage | Page 500 | Page d'erreur 500 (cible de ErrorController). Compose pageSettings. Exclue du sitemap et de la recherche par type (elle a un template, donc pas couverte par la regle du template). |
genericSection | Section generique | Composition de layout. Deux proprietes : hero (BlockList Custom - Bloc List - Hero, n'autorise que heroBlock, max 1) rendue en pleine largeur au-dessus, puis blocks (BlockGrid). Rendu : sections/genericSection.cshtml. |
heroBlock | Heros | Bloc de bande d'en-tete (titre + sous-titre + texte RTE + image/couleur de fond + lien). Hors du BlockGrid de base : utilise uniquement via la BlockList hero de genericSection (et blogPage). Vue blocklist/Components/heroBlock.cshtml, style base/blocks/_hero.scss (.block--hero, transparent par defaut pour laisser voir le bokeh fixe PEL). |
blogPage | Blog | Page de listing d'articles. Compose pageSettings. Template BlogPage. Enfants : articlePage, articleFolder. Ajoutable sous homePage et freeContentPage. Proprietes : hero (BlockList Heros), blogDisplay (Contentment : pagination / infinite), blogPageSize (Numeric, defaut 15), blogSortOrder (Contentment : datedesc / manual). Rendu serveur : filtres par tag (?tags=), pagination (?page=N) ou infinity scroll ; cartes via partial par theme layout/{theme}/_articleCard.cshtml (PEL = feed pel-website, APOL = card apol-website). La vue agrege les articlePage descendants (Descendants<ArticlePage>()), donc les articles ranges dans des articleFolder remontent aussi. |
articlePage | Article | Article de blog. Compose pageSettings. Template ArticlePage. Parent : blogPage ou articleFolder. Proprietes : articleDate (DatePicker), articleImage (ImageMediaPicker cover), articleIntro (Textarea chapo), articleAuthor (Textbox), articleTags (Tags, filtrage du listing), articleBody (BlockGrid dedie Custom - Block Grids - Article). |
articleFolder | Dossier d'actualites | Regroupe des actualites (rangement back-office, ex. par annee/mois), sur le modele d'eventFolder. Sans template (non rendu, exclu sitemap/recherche). ListView. Recursif (enfants : articleFolder, articlePage). Ne filtre pas l'affichage public : le blog liste tous les articlePage descendants tries par date. Compose felixMigrationContent (temporaire, migration felix). |
latestArticlesBlock | Dernieres actualites | Bloc BlockGrid (element, Blocks/Common, ajoute a CustomBlockGridsBase) : affiche les derniers articles d'un blog en cartes par theme. Proprietes : title, blog (ContentPicker ; si vide, le 1er blogPage du site), count (defaut 3), showAllLink (bouton « Voir toutes les actualites »), largeLayout (TrueFalse : cartes en 2 colonnes au lieu de 4). Vue blockgrid/Components/latestArticlesBlock.cshtml (reutilise layout/{theme}/_articleCard.cshtml ; colonnes de carte passees via ViewData["cardCols"], defaut = 4 colonnes par theme). |
comingEventsBlock | Prochains evenements | Bloc BlockGrid (element, Blocks/Common, groupe Modules dynamiques de CustomBlockGridsBase, cle b10c0022) : affiche les prochains evenements d'un listing. Jumeau evenementiel de latestArticlesBlock. Proprietes : title, eventsSource (ContentPicker vers un eventsPage/eventFolder ; si vide, le 1er eventsPage du site), count (defaut 3), layout (Contentment Data List - Events Layout : horizontal/vertical), showAllLink (bouton « Voir tous les evenements »). Ne garde que les evenements a venir (EffectiveEnd() >= now), tri DateStart croissant. Vue blockgrid/Components/comingEventsBlock.cshtml -> partagee blocks/comingEventsBlock.cshtml : horizontal = cartes layout/{theme}/_eventCard.cshtml ; vertical = liste compacte layout/_eventCompactItem.cshtml (sans image, Bootstrap seul). |
eventsPage | Evenements | Page listant les evenements. Compose pageSettings. Template EventsPage. ListView (ListViewContent). Enfants : eventFolder, eventPage. Ajoutable sous homePage/freeContentPage. Proprietes : hero, defaultView (Contentment : agenda/list/map), showPastEvents, mapCenterLat/mapCenterLng/mapZoom, subscribeHelpLink (Single URL Picker, optionnel : page expliquant comment s'abonner au calendrier ; si renseignee, un "?" apparait a cote du lien "S'abonner"). Lu en untyped dans la vue pour ne pas dependre de la regeneration ModelsBuilder. Vues (une page, ?view=, ?cat=, ?commune=), toutes faites : liste (server, cartes _eventCard par theme, a venir/passes), agenda (FullCalendar, lazy-import, alimente par le JSON inline #events-data) et carte (Google Maps, montee cote client par scripts/modules/events.js) ; la vue carte est conditionnee a une cle Google Maps (#events-map-wrap[data-key]) - sans cle, elle reste vide. Flux iCal ~/events/. |
eventFolder | Dossier d'evenements | Regroupe des evenements (ex. par commune : Belmont, Pully…). Sans template (non rendu, comme pageFolder). ListView. Enfants : eventFolder (recursif), eventPage. Les vues agregent les eventPage descendants. |
eventPage | Evenement | Un evenement. Compose pageSettings. Template EventPage. Parent : eventsPage/eventFolder. Proprietes : dateStart/dateEnd (DatePickerWithTime), allDay ; locationName/address/latitude/longitude (carte + iCal) ; image, intro, body (RTE), link ; category (Tags, filtrage). |
directoryPage | Annuaire | Page dediee, calquee sur le patron eventsPage. Compose pageSettings. Template DirectoryPage. ListView. Propriete showSearch (Truefalse, defaut decoche - meme piege que directoryBlock, voir plus bas). Enfants : externalContactFolder et externalContact (un contact peut donc vivre directement sous la page, sans dossier). Ajoutable sous homePage et freeContentPage. |
profilePage | Mon profil | Page « Mon profil » : le collaborateur connecte y voit sa fiche et met a jour les deux seuls champs que la sync AD n'ecrit jamais. Compose pageSettings, aucune propriete propre. Template ProfilePage. Ajoutable sous homePage. Lecture seule : prenom, nom, e-mail, fonction (ils viennent de l'AD). Modifiable : birthDate et photo. Le formulaire poste vers MemberProfileSurfaceController (antiforgery) qui ecrit via IMemberProfileService uniquement sur le membre connecte - le formulaire ne transporte aucun identifiant, la cle vient de la session. Photos deposees dans le dossier media « Photos des collaborateurs » (cree au premier envoi), JPG/PNG/WEBP, 5 Mo max. Sans membre connecte, la page affiche une invite au lieu du formulaire. |
externalContactFolder | Dossier de contacts | Regroupe des contacts externes (ex. par commune ou par service), sur le modele d'eventFolder. Recursif (peut contenir d'autres externalContactFolder), sans template (non rendu, exclu sitemap/recherche) et sans propriete. Son nom devient le titre de groupe a l'affichage. |
externalContact | Contact externe | Une fiche de contact hors personnel (commune, partenaire, service). Sans template. Proprietes : contactName (obligatoire), organisation, function, email, phone, mobilePhone, address, zip, locality, photo. Parent : directoryPage/externalContactFolder. Lu via IExternalContactService (voir extensibility.md) - jamais un intranetMember (voir Point de vigilance ci-dessous). |
exampleSection | Section d'exemple | Section de demonstration (titre + elements). |
exampleElement | Element d'exemple | Element de bloc de demonstration (contenu + titre). |
titleBlock | Titre | Bloc titre (texte + taille via DropdownHeadingSizes). |
cookiesSettings | Cookies Settings | Composition : textes/labels du popup de consentement cookies. |
marketingSettings | Marketing Settings | Composition : ID Google Analytics 4. |
footerLinkColumn | Pied de page - Colonne de liens | Element de bloc : titre + liens (MultiUrlPicker). Propriete Home footerLinkColumns. |
footerCommuneElement | Pied de page - Element commune | Element de bloc : lien (MultiUrlPicker) + commune/theme (Data List - Footer commune, Contentment DataList cle/valeur : nom lisible « Lavaux » -> valeur = classe CSS btn-lavaux, utilisee telle quelle par le partial). Label de bloc = nom du lien (${link[0].name}). Propriete Home footerCommunes, rendu dans le bandeau « police de proximite » du footer APOL. |
Les example* et titleBlock sont des exemples a adapter/supprimer dans un projet aval.
Le noeud Home porte les donnees du footer (onglet « Pied de page ») : coordonnees (footerStreet, footerZipCode, footerLocality, footerPhone, footerEmail, footerOpeningHours = RTE rendu via @Model.FooterOpeningHours — IHtmlEncodedString), footerSocialLinks, footerLinkColumns (BlockList de footerLinkColumn) et footerCommunes (BlockList de footerCommuneElement).
DataTypes (uSync/v17/DataTypes/)
Les 51 DataTypes sont ranges dans des conteneurs (dossiers) cote backoffice via l'element <Folder> de chaque .config (les fichiers restent a plat sur le disque ; Level="2"). uSync recree les conteneurs a l'import (au demarrage, groupe Settings). Dossiers : Dropdowns, DatePickers, Pickers, Media, Uploads, Labels, ListViews, Fields, Blocks. Pour deplacer un DataType, changer son <Folder> (ou le glisser dans le backoffice, ExportOnSave re-serialise).
Bibliotheque d'editeurs preconfigures, prete a l'emploi :
- Pickers media :
MediaPicker,ImageMediaPicker,MultipleMediaPicker,ImageCropper, variantes legacy. - Pickers contenu/URL :
ContentPicker,MemberPicker,SingleURLPicker,MultiURLPicker. - Champs :
Textstring,Textarea,RichtextEditor,Numeric,Decimal/DecimalPositiveOnly,Truefalse,Dropdown/DropdownMultiple,DropdownHeadingSizes,Radiobox,CheckboxList,Tags,DatePicker/DatePickerWithTime,ApprovedColor.RichtextEditor(Tiptap,ca90c950-...) autorise des blocs inline (extensionUmb.Tiptap.Block+ toolbarBlockPicker) : tous les blocs du projet y sont déclarés (RichtextEditor.configblocks). Stockage ={ markup, blocks:{ layout["Umbraco.RichText"], contentData, settingsData } }, la position du bloc dans le markup =<umb-rte-block data-content-key="GUID">.- Rendu des blocs (convention) : le markup d'un bloc vit une seule fois dans un partial partagé
Views/Partials/blocks/{alias}.cshtml(typé sur le modèle du bloc, ex.UmbracoViewPage<ButtonBlock>). Les trois contextes délèguent à ce partagé viaHtml.PartialAsync(..., Model.Content as {Modèle}):blockgrid/Components/{alias}.cshtml(modèleBlockGridItem),blocklist/Components/{alias}.cshtml(BlockListItem, ex. heroBlock), etrichtext/Components/{alias}.cshtml(RichTextBlockItem) pour le rendu inline en RTE. Un bloc sansrichtext/Components/{alias}.cshtmlest insérable dans un RTE mais disparaît silencieusement au rendu. Exception :oneColumnLayoutBlock(bloc de structure à areas) — sa version RTE rend seulement la coquille stylée (les areas n'existent pas dans un RTE).
- BlockGrid / BlockList :
CustomBlockGridsBase(le heroBlock en a ete retire),Custom - Block Grids - Article(BlockGrid dedie aux articles = Base sans hero/navigation/socialNetworks ; Definitiona17c1e00-b106-...-01),Custom - Bloc List - Hero(BlockListheroBlockseul, max 1 ; Definitionb10c0005-...-d1),CustomBlocListExampleElement. - Contentment (blog) :
Data List - Blog Display(pagination / infinite ;...-02),Data List - Blog Sort Order(datedesc / manual ;...-03). - Contentment (evenements) :
Data List - Events Layout(horizontal / vertical ;da7a0004-...-04) — affichage ducomingEventsBlock. - Contentment (horaires) :
Data List - Hours Format(list / paragraph ;da7a000a-...-0a) — affichage duopeningHoursBlock. - Contentment (galerie) :
Data List - Gallery Sort(manual / name / date ;da7a000b-...-0b) etData List - Gallery Display(grid / masonry ;da7a000c-...-0c) — tri et affichage dugalleryBlock. - BlockList :
Custom - Bloc List - Opening Hours(da7a0009-...-09, dossierCustom) — lignesopeningHourItemduopeningHoursBlock. - BlockList :
Custom - Bloc List - Slider(da7a0005-...-05, dossierCustom) — diapositivessliderItemdusliderBlock. - BlockList :
Custom - Bloc List - Button(da7a0008-...-08, dossierCustom) — boutonsbuttonItemdubuttonGroupBlock. - Contentment (card) :
Data List - Card Content Position(da7a0006-...-06: below/top/center/bottom),Data List - Image Ratio(da7a0007-...-07:ratio-16x9,ratio-2x1… classes CSS emises) etData List - Text Align(da7a000d-...-0d: Gauche/Centré/Droite →text-start/text-center/text-end, dossierDropdowns) — reglages ducardBlock(titleHorizontalAlignpour ce dernier). - Labels (lecture seule) :
LabelString,LabelInteger,LabelBigint,LabelDecimal,LabelDatetime,LabelTime,LabelBytes,LabelPixels. - ListViews :
ListViewContent,ListViewMedia,ListViewMembers. - Uploads media :
UploadFile,UploadArticle,UploadAudio,UploadVideo,UploadVectorGraphics. seo:Umbraco.TextBoxconfigure (maxChars 20).- Contentment (
Umbraco.Community.Contentment) :Data List - Footer commune,Data List - Button Style,Data List - Separator Size,Data List - Document Sort(options de tri dudocumentsBlock:dateDesc/dateAsc/manual) (Umbraco.Community.Contentment.DataList, sourceUserDefinedDataListSource+ editorDropdownListDataListEditor, UI aliasUmb.Contentment.PropertyEditorUi.DataList). - Button Pickers (
ButtonPicker*.config, dossierButton Pickers) : 13 datatypes du property editor custom ButtonPicker (schemaUmbraco.TextBox, UISpektrum.PropertyEditorUi.ButtonPicker- voirbackoffice.md) pour les reglages du bloconeColumnLayoutBlock(alignement, largeur, filtre, arrondi, ombre, animation...). Config{ buttons:[{label,value}], defaultValue, small };value= classe CSS emise dans la vue. La listebuttonss'edite dans le backoffice via l'editeur de config repeaterSpektrum.PropertyEditorUi.ButtonPickerConfig(une ligne = libelle + classe-css), ou directement dans le.configuSync.
Organisation des DataTypes en dossiers (<Folder> uSync)
Les DataTypes sont ranges dans l'arbre backoffice via l'element <Folder> (dans <Info>). Convention retenue : Dropdowns, Labels, Custom (BlockGrid/BlockList Custom - ...), Pickers (contenu/membre/URL), Media (media pickers + cropper), List Views, Uploads, Fields (champs de base : texte, RTE, numerique, booleen, dates, couleur, tags, seo...).
Convention : listes d'options = Contentment DataList cle/valeur
Pour toute liste d'options d'un bloc (style de bouton, taille, etc.), ne pas utiliser Umbraco.DropDown.Flexible (le libelle affiche = la valeur stockee). Utiliser une Contentment DataList (UserDefinedDataListSource) avec des paires name/value : name = libelle francais affiche a l'editeur (ex. « Primaire »), value = valeur technique stockee et utilisee dans les vues (ex. primary). Modele de reference : Data List - Footer commune. Rangees dans le dossier Dropdowns.
Label de bloc (BlockList/BlockGrid) en Umbraco 17 : le nouveau backoffice ne supporte plus la syntaxe AngularJS {{ ... }} (ni les filtres type | truncate). Deux formes :
- valeur simple d'une propriete : marqueur UMB
{=alias}(ex."label": "{=columnTitle}"). - valeur imbriquee / propriete objet (ex. nom d'un
MultiUrlPicker) : expression${alias[0].name}(ex."label": "${link[0].name}").{=link}ne conviendrait pas ici (il serialise l'objet ->[object Object]). - fallback / logique : l'expression
${...}est evaluee en JS, donc||et ternaires marchent. Ex.Custom - Bloc List - Nav Menu:"label": "${navMenuTitle || (navMenuLink && navMenuLink[0] && navMenuLink[0].name) || ''}"-> affiche le Titre, sinon le nom du lien, sinon vide (Umbraco retombe alors sur le nom du type de bloc).
Navigation de l'en-tete (Home > En-tete)
Le menu principal est une BlockList headerNavMenuLinks sur HomePage, contenant des navMenuElement (navMenuLink + navMenuTitle + sous-menu). Deux formes de sous-menu, exclusives a l'affichage (priorite aux sections) :
- Liste plate :
navSubmenuLinks(MultiURLPicker) -> dropdown une colonne. Utilise par APOL (_headerNav.cshtml) et PEL. - Sections (méga-menu) :
navMenuSections, BlockList (Custom - Bloc List - Nav Menu Sections, max 3) de l'element typenavMenuSection(sectionTitleTextstring +sectionLinksMultiURLPicker). Chaque bloc = une colonne (titre + liens). Rendu dans les deux thèmes (chacun son style, fidèle à son site source) :- PEL :
Views/Partials/layout/pel/_header.cshtml, carte blanche en grille (.sub-nav--mega/.sub-nav__col/.sub-nav__title). - APOL :
Views/Partials/layout/_headerNav.cshtml, panneau gris pleine largeur en colonnes flottantes (.full-width+.dropdown-columns+<ul>par section, titreli.level-3), repris d'apol-website. - Cf.
.claude/docs/architecture/frontend-build.md(Header PEL / Header APOL).
- PEL :
Résolution des redirections dans le menu : un lien de menu qui pointe vers une page pageFolder/freeContentPage portant un redirectTarget (redirection 302, cf. RedirectTargetHandler dans extensibility.md) est réécrit directement vers la cible de la redirection — évite le rebond 302. Helper Link.EffectiveUrl(IPublishedContentQuery) (Extensions/LinkExtensions.cs), appliqué aux 3 niveaux de liens dans _headerNav.cshtml (desktop, apol+pel) et apol/_headerMobile.cshtml (menu mobile).
Un {{ ... }} copie depuis un projet Umbraco <= 13 s'affiche litteralement dans l'editeur.
Bloc de layout « Une colonne » (oneColumnLayoutBlock)
Porte de pel-website. Bloc de layout BlockGrid, dans le groupe Layout Blocks de Custom - Block Grids - Base : il expose une area center (colSpan 12, tous blocs autorises) et un settings element type blockGridSettingsColumn (~21 reglages : largeur/contenu, alignement, fond couleur/image, filtre, arrondi, ombre, motif, min-height, animation, ancre, classes...). Le bloc de contenu oneColumnLayoutBlock n'a aucune propriete (tout est dans les settings). Vue blockgrid/Components/oneColumnLayoutBlock.cshtml, typee BlockGridItem<OneColumnLayoutBlock, BlockGridSettingsColumn> : les valeurs des reglages sont les classes CSS emises sur la section. Redimensionnement pilote par columnSpanOptions + rowMaxSpan de l'entree du bloc dans le .config du BlockGrid (une liste vide = bloc non redimensionnable). SCSS structurel : base/blocks/_layoutColumn.scss (classes dynamiques en safelist PurgeCSS, cf. vite.config.helper.js). Reglages bases sur le property editor ButtonPicker (cf. backoffice.md).
Blocs Accordéon & Onglets (accordionBlock, tabsBlock)
Portés d'apol/pel-website. Chacun = un bloc parent (accordionBlock/tabsBlock, groupe Content Blocks du BlockGrid de base) avec une propriété BlockList d'items (Custom - Bloc List - Accordion/- Tabs) ; chaque item (accordionItem/tabItem) a title (TextBox), body (RichText) et, pour l'accordéon, open (TrueFalse, ouvert par défaut). Le body est un RichText (simplification vs le BlockGrid imbriqué des sites de réf). Rendu Bootstrap 5 (accordion collapse / tabs tab, JS déjà chargé via import * as bootstrap), avec style par thème : la vue Component (blockgrid/Components/{accordion,tabs}Block.cshtml) dispatche vers layout/{theme}/_{accordion,tabs}Block.cshtml. APOL = accordéon bordé cyan + accent gauche, onglets nav-tabs bordés MAJ ; PEL = accordion-flush bleu, onglets nav-underline nav-fill. SCSS : themes/{apol,pel}/layout/_accordion.scss + _tabs.scss (l'essentiel du look vient des overrides Bootstrap $accordion-*/$nav-* de chaque thème).
accordionBlock a aussi une propriété flatten (TrueFalse, « Toujours ouvert (non repliable) »). Quand elle est cochée : le conteneur reçoit la classe accordion--static, chaque en-tête est rendu en <button disabled> (sans data-bs-toggle, non-focusable), tous les panneaux sont forcés ouverts (show) et le open par item est ignoré — le bloc devient une liste de sections statiques (le look du thème est conservé, seul le chevron est grisé via .accordion--static, cf. _accordion.scss). Les vues lisent le flag en untyped (Model.Value<bool>("flatten")) pour compiler avant régénération ModelsBuilder ; défaut décoché → rétrocompatible.
Blocs Annuaire & Anniversaires (directoryBlock, birthdaysBlock)
Trois blocs pilotés par le personnel (intranetMember) via IMemberDirectoryService, sans contenu Umbraco propre — les deux premiers dans le groupe Content Blocks de Custom - Block Grids - Base, le troisième dans Modules dynamiques :
directoryBlock(Annuaire) :title(Textstring),showSearch(Truefalse défaut coché — DataTypeTruefalseDefaultTrue, comme surdirectoryPage: le champ de recherche est visible dès que le bloc est posé ; l'éditeur décoche pour le masquer). Le bloc est un aperçu : il ne filtre pas, et son formulaire renvoie vers la pagedirectoryPageavec?q=. Sans pagedirectoryPagedans l'arbre, le champ de recherche est masqué. Vueblockgrid/Components/directoryBlock.cshtml-> partagéeViews/Partials/blocks/_directory.cshtml: deux onglets Collaborateurs / Contacts externes (membresintranetMemberviaIMemberDirectoryService, contacts externes viaIExternalContactService). Le même partial sert le templateViews/DirectoryPage.cshtml, seul point où la recherche s'exécute (?q=, filtre les deux onglets : nom, fonction, matricule, incorporation côté collaborateurs ; nom, organisation, fonction, localité côté contacts).birthdaysBlock(Anniversaires) :title,showTomorrow(Truefalse). Anniversaires du jour (+ demain si coché), filtrés jour+mois, et visibles (showBirthday, voir MemberTypes ci-dessous). Rendu repris du widget felix : accroche « Souhaitez un bon anniversaire à », âge atteint en pastille, et section « Demain » affichée même vide (« Aucun anniversaire »). Vueblockgrid/Components/birthdaysBlock.cshtml-> une personne =blocks/_birthdayPerson.cshtml(avatar via_memberAvatar.cshtml, âge viaDirectoryRules.AgeOn).dutyOfficerBlock(Permanence), cléb10c0027, groupe Modules dynamiques :title(Textstring, défaut « Permanence ») etdutyOfficer(Member Picker). Porté du widget felixWidgetPermanence: affiche l'officier de permanence et ses numéros en lienstel:, repris de sa fiche membre — l'éditeur ne ressaisit rien, il repointe le picker. Lu en untyped dans la vue (indépendant de la régénération ModelsBuilder) ; le membre est résolu par sa clé viaIMemberDirectoryService.GetByKey, qui ignore volontairementshowInDirectory(la personne a été désignée explicitement) mais écarte un membre désactivé. Vueblockgrid/Components/dutyOfficerBlock.cshtml.
Convention d'état vide (blocs personnel) : ces blocs restent toujours visibles avec leur titre et affichent un message discret (<p class="text-muted mb-0">) quand ils n'ont rien à montrer — l'éditeur voit que le bloc est bien posé plutôt que de le voir disparaître. Messages : « Aucun anniversaire » (birthdaysBlock), « Aucune permanence désignée » (dutyOfficerBlock), « Aucune vacance à venir » (vacationsBlock timeline ; la vue calendrier reste visible en affichant un calendrier vide).
Les deux réutilisent le partial d'avatar Views/Partials/blocks/_memberAvatar.cshtml (photo si présente, sinon initiales) côté collaborateurs ; les contacts externes ont leur propre partial Views/Partials/blocks/_contactAvatar.cshtml. Vues en lecture untyped sur Model.Content (pas de dépendance aux modèles régénérés). Services enregistrés dans SiteComposer. Verrouillage du front : MemberGateMiddleware (voir extensibility.md).
Blocs Citation & Groupe de boutons (quoteBlock, buttonGroupBlock)
Groupe Blocs de contenu de CustomBlockGridsBase, rendu Bootstrap commun.
quoteBlock(Citation) (b10c0025) : porté d'apol-website (minimaliste). Propriété uniquequote(RichtextEditor) →blocks/quoteBlock.cshtmlrend<blockquote class="blockquote block--quote">. SCSSstyles/base/blocks/_quote.scss(bordure gauche + typographie, fidèle APOL). Pas d'auteur ni image (fidèle source).buttonGroupBlock(Groupe de boutons) (b10c0026) +buttonItem(b10c0026-...-a0) : porté de pel-website (widgetButtonGroup). Le bouton est calqué surbuttonBlock(linkSingleURLPicker,style=Data List - Button Styleréutilisé →btn--primary/secondary/outline/link,iconTextstring) via la BlockListCustom - Bloc List - Button. Le groupe :align(Button Picker - Horizontal Alignréutilisé, émetjustify-content-*),merged(→btn-group),stacked(→btn-group-vertical).blocks/buttonGroupBlock.cshtml: conteneurd-flex flex-wrap @align+ conteneur interne (btn-group/btn-group-vertical/d-flex gap-2). Aucun SCSS custom (Bootstrap +_button.scssexistant).
Blocs Slider & Card (sliderBlock, cardBlock)
Portés de pel-website (widgetSlider, widgetCard), groupe Blocs de contenu de CustomBlockGridsBase, rendu Bootstrap commun (pas de split par thème).
sliderBlock(Slider) (b10c0023) : carrousel Splide de diapositives typéessliderItem(b10c0023-...-a0:image,title,textRTE,link), via la BlockListCustom - Bloc List - Slider. Réglages (onglet Réglages) :perPage(1-4, breakpoints responsives auto),interval(autoplay ms, 0 = off),loop,showArrows,showPagination,height(vh, 0 = auto),removeGap,animation/delay(ButtonPickers scroll-anim). Les options Splide sont construites côté serveur dans l'attributdata-splide(comme pel) ;blocks/sliderBlock.cshtmlproduit le markup.splideetscripts/modules/blocks/slider.js(lazy, sur.js-slider) monte Splide en lisantdata-splide. SCSSstyles/base/blocks/_slider.scss(modeslider--auto= ratio 16:9 par défaut si pas de hauteur fixe). Pas d'extension vidéo (diapositives image). Simplification vs pel : diapositive typée au lieu d'un BlockGrid imbriqué (même esprit que l'accordéon RTE).cardBlock(Carte) (b10c0024) : carte de contenu fidèle auwidgetCardpel. Propriétés :title/titleSize(DropdownHeadingSizes),text(RTE),imageetvideo(fond, MediaPicker3),link(carte cliquable = stretched-link),button(SingleURLPicker, au-dessus du lien viaposition-relative),contentPosition(below/top/center/bottom),ratio(Data List - Image Ratio),filter/contrast/shadow/borderRadius/animation/delay(ButtonPickers réutilisés du bloc « Une colonne », déjà stylés),bgColor(ApprovedColor),isTextSmaller(Truefalse, texte réduit),titleHorizontalAlign(Data List - Text Align, alignement du titre).blocks/cardBlock.cshtml: modebelow= image (au ratio) puis corps ; mode superposé = corpsposition-absolutealigné sur le cover. Vidéo de fond enautoplay muted loop(comme le cover deoneColumnLayoutBlock, pas de lecture au survol). SCSSstyles/base/blocks/_card.scss(classesratio-*x*en safelist PurgeCSS, filtres/overlay, zoom au survol).
Bloc Horaires d'ouverture (openingHoursBlock)
Porté de pel-website (widgetOpeningHours), groupe Blocs de contenu de CustomBlockGridsBase (clé b10c0029). Grille d'horaires simple : title (Textstring), items (BlockList Custom - Bloc List - Opening Hours d'openingHourItem : day + time, deux Textstring en texte libre) et hoursFormat (Data List list (défaut) / paragraph). Rendu Bootstrap commun (pas de split par thème, pas de SCSS custom — la source n'en avait pas) : list = <ul class="list-group list-group-flush"> (jour à gauche, horaire à droite), paragraph = un <p> avec <br>. Vue blockgrid/Components/openingHoursBlock.cshtml -> partagée blocks/openingHoursBlock.cshtml (typée OpeningHoursBlock, items OpeningHourItem). Écarts assumés vs source : les sources d'horaires affiliate (nœud société, concept absent du template) et placeId (horaires Google Places en direct) sont volontairement écartées — pas de dépendance à une clé Google (cohérent avec le mapBlock en iframe sans clé). Ajout propre : title (la source n'en avait pas).
Options des blocs média (galleryBlock, imageBlock, videoBlock)
Propriétés ajoutées aux blocs média communs (comblent des écarts vs les sources apol/pel ; vues typées sur les modèles générés) :
galleryBlock:sortOrder(Data List - Gallery Sort: manuel / nom / date, tri côté serveur),display(Data List - Gallery Display:gridgrille carrée par défaut oumasonrycolonnes CSS à ratio naturel),showCaptions(Truefalse : nom du média en surimpression,.gallery__caption). Lightbox Splide inchangée.imageBlock:enableFullscreen(Truefalse) — réutilise l'infra lightbox de la galerie (.js-gallery+[data-lightbox]+scripts/modules/blocks/gallery.js), aucun JS propre : l'image s'ouvre en plein écran au clic.videoBlock:ratio(Data List - Image Ratioréutilisé) — contraint la vidéo/embed à un format via.block--video-frame.ratio-*x*(vide = ratio d'origine, 16:9 pour un lien) ;mute(Truefalse) — coupe le son, forcé quand la lecture automatique est active (exigence navigateurs) ;hideControls(Truefalse) — masque les contrôles du lecteur, vidéo hébergée uniquement (<video controls>natif ; sans effet sur un embed iframe).
SCSS : styles/base/blocks/_gallery.scss (variante masonry + overlay légende) et _video.scss (.block--video-frame + classes ratio-*x*). Aucune classe à ajouter au safelist PurgeCSS (les modificateurs sont littéraux dans les vues ; ratio-*x* déjà safelisté).
Réglages de bloc blockSettings (ancre + classes CSS)
Element-type de settings partagé (b1005e77-..., onglet Extension) : deux propriétés, anchorId (Textstring → attribut id du bloc, pour le lien profond) et customClasses (Textstring → classes CSS libres, pour de la personnalisation fine sans nouveau réglage dédié). Comme oneColumnLayoutBlock, il vit côté Settings du bloc, pas dans le contenu : câblé via settingsElementTypeKey sur chaque entrée de bloc des BlockGrids Custom - Block Grids - Base et Custom - Block Grids - Article, ainsi que sur le bloc du BlockList Custom - Bloc List - Hero.
Exclusions : oneColumnLayoutBlock garde son settings riche propre blockGridSettingsColumn (qui porte déjà anchorId/customClasses) ; les items imbriqués (sliderItem, accordionItem, tabItem...) n'en ont pas.
Rendu centralisé : Views/Partials/blockgrid/items.cshtml lit les réglages via item.Settings as BlockSettings et émet id/les classes sur le wrapper d'item, pour tous les blocs BlockGrid (un bloc dont le settings n'est pas blockSettings, comme oneColumnLayoutBlock, donne un cast null → aucun ajout, il rend sa propre ancre). heroBlock (rendu en BlockList) : blocklist/Components/heroBlock.cshtml lit Model.Settings as BlockSettings et passe anchorId/customClasses au partial partagé blocks/heroBlock.cshtml via ViewData. Ces accès typés supposent les modèles régénérés (ModelsBuilder SourceCodeAuto, cycle en deux temps au premier import).
Limite connue : le RTE-inline (blocs insérés dans un RichtextEditor) n'est pas couvert — un bloc inséré dans un RTE n'a pas de settings, donc anchorId/customClasses restent sans effet dans ce contexte.
Module Annonces (announcementsPage, announcementPage)
Porté d'apol-website et pel-website (mêmes doctypes des deux côtés). Système d'annonces éditoriales affichées en pop-up ou en bande rouge en haut de toutes les pages, bornées dans le temps. Ce ne sont pas des pages routables : les deux doctypes n'ont aucun template (jamais rendus à leur URL) — ils sont injectés par le layout (voir extensibility.md).
announcementsPage(Annonces) (a11ce000-...-01) : conteneur, vue liste (List View - Content), icône mégaphone, compositionpageSettings, aucune propriété propre. Ajouté à laStructuredehomePage(un nœud par site, sous la home). N'autorise queannouncementPagecomme enfant.announcementPage(Annonce) (a11ce000-...-02) : compositionpageSettings. Onglet Contenu :title(Textstring, obligatoire),content(RichtextEditor, obligatoire). Onglet Paramètres :display(Data List - Announcement Display :popup/band),broadcasting(Data List - Announcement Broadcasting :oncePerVisit/oncePerVisitor),startDate/endDate(Date Picker with time, invariants),targetNodes(Multi Node Tree Picker, invariant — vide = tout le site, sinon limite aux pages ciblées et à leurs descendants).
DataTypes créés : Data List - Announcement Display et Data List - Announcement Broadcasting (Contentment DataList clé/valeur, dossier Dropdowns) ; Multi Node Tree Picker (éditeur natif Umbraco Umbraco.MultiNodeTreePicker, dossier Pickers, générique et réutilisable — aucun package tiers).
Module Vacances du personnel (PEL)
Vacances des collaborateurs, PEL uniquement. Stockées sur la fiche membre (pas de nœud de contenu), saisies via un éditeur backoffice custom (voir backoffice.md → VacationsEditor).
- Champ
vacationssurintranetMember(onglet « Vacances ») : DataTypeVacances(EditorAlias Umbraco.TextArea,EditorUIAlias Spektrum.PropertyEditorUi.VacationsEditor, dossierCustom). Valeur = JSON maison[{ year, entries:[{ title, start, end, note }] }]. Organisation en dossiers par année (créés à la main), historique conservé. - Lecture :
MemberDirectoryService.Mapparse le JSON →DirectoryMember.Vacations(périodes aplaties,Yeargardé).GetUpcomingVacations(take)= en cours/à venir ;GetAllVacations()= toutes (passé + futur, pour le calendrier). Voirextensibility.md. - Bloc
vacationsBlock(Vacances) (b10c0028, groupe Modules dynamiques deCustomBlockGridsBase) :title,view(Data Listcalendar/timeline, défaut calendrier) etcount(timeline). Calendrier = FullCalendar (réutilise la lib des événements), une vacance = un événement journée entière, clic → popup de détail (module JSscripts/modules/blocks/vacations.js, données JSON inline, safelistvacations-popup). Timeline = rendu serveur réutilisant le visuelevents-timeline(année/mois). Pas d'avatar. Vueblockgrid/Components/vacationsBlock.cshtml(typéeVacationsBlock). PEL uniquement : alias dansPelBlocks(BlockGridSkinFilterHandler). - Profil : section « Mes vacances » (PEL), historique groupé par année, lecture seule (
Views/ProfilePage.cshtml).
Property editors custom (App_Plugins/)
Editeurs de propriete maison, reutilisables pour creer des DataTypes dans le backoffice (voir aussi modules/backoffice.md) :
- ButtonPicker (
App_Plugins/ButtonPicker/) — property editor UISpektrum.PropertyEditorUi.ButtonPicker(schemaUmbraco.TextBox) : un groupe de boutons single-select ou la valeur stockee est la classe CSS du bouton choisi (emise par les vues de blocs). La liste des boutons ({ label, value }), ladefaultValueet l'option "boutons compacts" se configurent sur le DataType via l'editeur de configSpektrum.PropertyEditorUi.ButtonPickerConfig(repeater libelle / classe). 100 % frontend (aucun backend C#). Aucun DataType prelivre : creer unData Typebase sur "Button Picker" et y definir les boutons.
MediaTypes / MemberTypes / Templates
- MediaTypes :
image,file,folder,umbracoMediaArticle,umbracoMediaAudio,umbracoMediaVideo,umbracoMediaVectorGraphics,mediaLink. Les libellés des types natifs sont francises (alias inchanges, donc aucun impact code) : Fichier, Dossier, Document (umbracoMediaArticle— « Document » et non « Article », qui preterait a confusion avec les actualites), Image vectorielle (SVG), Video. Divergence assumee avec le templateupstream: a garder en cas de conflit de merge.mediaLink(« Lien ») : média sans fichier, juste une propriétélink(SingleURLPicker : page/media/URL externe) +documentTitle/documentDescription. Remplace le type de média « Link » de felix (WidgetDocumentRetreiveFilesAndLinks). LedocumentsBlockle rend comme un lien (icon-file-link, ouvre l'URL) au lieu d'un téléchargement. ComposefelixMigration(pour l'import).FileportedocumentTitle(TextBox) etdocumentDescription(TextArea) — titre/description affichables d'un document (repris de felix, utilisés par ledocumentsBlock).- Composition média temporaire
felixMigration(cléf0000001-...-01, champfelixIdNumeric) composée dansImage/File/Folder: garde l'ancien id felix pendant la migration de contenu (mapping ancien→nouveau). À supprimer en fin de migration. Voirmigrations/felix/.
- Composition contenu temporaire
felixMigrationContent(cléf0000002-...-02, champfelixId) composée danseventPage/eventFolderetarticlePage/articleFolder: même rôle côté contenu (recâblage des parents des arbres events et actualités + idempotence). À supprimer en fin de migration. - MemberTypes :
member(standard) ;intranetMember(personnel) — champsfirstName,lastName,function,photo(ImageMediaPicker),birthDate(DatePicker sans heure, propriété sensible),phone,mobilePhone,matricule,incorporation,showInDirectory(Truefalse, visible par défaut),showBirthday(Truefalse, visible par défaut - distinct deshowInDirectory: permet de figurer à l'annuaire sans exposer sa date de naissance ; commebirthDate/photo/showInDirectory, jamais écrit par la sync AD). Alimenté à terme par le SSO/AD ; en attendant, saisie backoffice ou seed de dev (/lm). Sert les modules annuaire et anniversaires viaIMemberDirectoryService. Porte aussimemberReadArticles(TextArea,CanEdit/CanViewà false) : les ids des actualités déjà ouvertes, en CSV, tenus à jour parIArticleReadService— repris de felix, voirbusiness-rules.md. - MemberGroups (
uSync/v17/MemberGroups/) :Utilisateurs intranet— groupe d'accès neutre pour le verrouillage du front (le découpage admin / lecture seule viendra avec le mapping des groupes AD). - Templates :
homepage,freecontentpage,notfoundpage,internalservererrorpage.
Langues (uSync/v17/Languages/)
fr(French) : langue par defaut (IsDefault=true).en-US: marqueeChange="Delete"dans uSync (en cours de suppression) - ne pas considerer comme active.- Le backoffice est en
fr-FR(DefaultUILanguage,appsettings.json).
Dictionnaire (uSync/v17/Dictionary/)
Cles d'interface multilingues, surtout pour le consentement cookies (cookies.*, cookiespopup*), le footer (footer.*), l'accessibilite (accessibility.skiptocontent, accessibility.noscript...) et des cles generiques. Le handler Dictionnaire uSync est en CreateOnly (n'ecrase pas les traductions existantes a l'import).
Icones des document types (<Icon>)
L'<Icon> d'un document type doit etre un alias existant dans le set d'icones du backoffice v17 (sinon icone cassee / fallback). Le nouveau backoffice (v14+) a change de set : des alias v13 ont disparu (ex. icon-blockquote, icon-address n'existent plus). En v17 les icones sont packagees comme fichiers icon-<nom>-<hash>.js dans Umbraco.Cms.StaticAssets : ~/.nuget/packages/umbraco.cms.staticassets/<version>/staticwebassets/umbraco/backoffice/packages/core/icon-*.js. Pour lister/verifier : ls .../packages/core/icon-*.js | sed -E 's#.*/##; s#-[A-Za-z0-9_]+\.js$##' | sort -u (v17.4.2 = 714 icones). Icones de blocs utilisees : icon-script, icon-umb-media, icon-tab, icon-navigation-horizontal, icon-home, icon-addressbook, icon-pictures-alt-2, icon-code, icon-video, icon-map-location, icon-share, icon-documents, icon-navigation-top.
Ajouter / modifier un document type
Backoffice -> uSync exporte (ExportOnSave="Settings") ; les modeles se regenerent (SourceCodeAuto). Voir ../architecture/patterns.md.

