Skip to content

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/)

AliasNomRole
homePagePage d'accueilRacine de site. Compose cookiesSettings, exampleSection, genericSection (+ autres compositions). Template homePage. Enfants autorises : freeContentPage, folder, searchPage, notFoundPage, internalServerErrorPage.
searchPagePage de recherchePage 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.
freeContentPagePage generiquePage 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.
folderDossierConteneur 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.
pageSettingsPage SettingsComposition SEO/sitemap partagee par les pages (meta description, masquage sitemap...).
notFoundPagePage 404Page 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).
internalServerErrorPagePage 500Page 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).
genericSectionSection generiqueComposition 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.
heroBlockHerosBloc 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).
blogPageBlogPage 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.
articlePageArticleArticle 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).
articleFolderDossier d'actualitesRegroupe 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).
latestArticlesBlockDernieres actualitesBloc 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).
comingEventsBlockProchains evenementsBloc 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).
eventsPageEvenementsPage 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/.
eventFolderDossier d'evenementsRegroupe des evenements (ex. par commune : Belmont, Pully…). Sans template (non rendu, comme pageFolder). ListView. Enfants : eventFolder (recursif), eventPage. Les vues agregent les eventPage descendants.
eventPageEvenementUn 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).
directoryPageAnnuairePage 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.
profilePageMon profilPage « 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.
externalContactFolderDossier de contactsRegroupe 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.
externalContactContact externeUne 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).
exampleSectionSection d'exempleSection de demonstration (titre + elements).
exampleElementElement d'exempleElement de bloc de demonstration (contenu + titre).
titleBlockTitreBloc titre (texte + taille via DropdownHeadingSizes).
cookiesSettingsCookies SettingsComposition : textes/labels du popup de consentement cookies.
marketingSettingsMarketing SettingsComposition : ID Google Analytics 4.
footerLinkColumnPied de page - Colonne de liensElement de bloc : titre + liens (MultiUrlPicker). Propriete Home footerLinkColumns.
footerCommuneElementPied de page - Element communeElement 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.FooterOpeningHoursIHtmlEncodedString), 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 (extension Umb.Tiptap.Block + toolbar BlockPicker) : tous les blocs du projet y sont déclarés (RichtextEditor.config blocks). 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é via Html.PartialAsync(..., Model.Content as {Modèle}) : blockgrid/Components/{alias}.cshtml (modèle BlockGridItem), blocklist/Components/{alias}.cshtml (BlockListItem, ex. heroBlock), et richtext/Components/{alias}.cshtml (RichTextBlockItem) pour le rendu inline en RTE. Un bloc sans richtext/Components/{alias}.cshtml est 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 ; Definition a17c1e00-b106-...-01), Custom - Bloc List - Hero (BlockList heroBlock seul, max 1 ; Definition b10c0005-...-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 du comingEventsBlock.
  • Contentment (horaires) : Data List - Hours Format (list / paragraph ; da7a000a-...-0a) — affichage du openingHoursBlock.
  • Contentment (galerie) : Data List - Gallery Sort (manual / name / date ; da7a000b-...-0b) et Data List - Gallery Display (grid / masonry ; da7a000c-...-0c) — tri et affichage du galleryBlock.
  • BlockList : Custom - Bloc List - Opening Hours (da7a0009-...-09, dossier Custom) — lignes openingHourItem du openingHoursBlock.
  • BlockList : Custom - Bloc List - Slider (da7a0005-...-05, dossier Custom) — diapositives sliderItem du sliderBlock.
  • BlockList : Custom - Bloc List - Button (da7a0008-...-08, dossier Custom) — boutons buttonItem du buttonGroupBlock.
  • 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) et Data List - Text Align (da7a000d-...-0d : Gauche/Centré/Droite → text-start/text-center/text-end, dossier Dropdowns) — reglages du cardBlock (titleHorizontalAlign pour 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.TextBox configure (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 du documentsBlock : dateDesc/dateAsc/manual) (Umbraco.Community.Contentment.DataList, source UserDefinedDataListSource + editor DropdownListDataListEditor, UI alias Umb.Contentment.PropertyEditorUi.DataList).
  • Button Pickers (ButtonPicker*.config, dossier Button Pickers) : 13 datatypes du property editor custom ButtonPicker (schema Umbraco.TextBox, UI Spektrum.PropertyEditorUi.ButtonPicker - voir backoffice.md) pour les reglages du bloc oneColumnLayoutBlock (alignement, largeur, filtre, arrondi, ombre, animation...). Config { buttons:[{label,value}], defaultValue, small } ; value = classe CSS emise dans la vue. La liste buttons s'edite dans le backoffice via l'editeur de config repeater Spektrum.PropertyEditorUi.ButtonPickerConfig (une ligne = libelle + classe-css), ou directement dans le .config uSync.

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).

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 type navMenuSection (sectionTitle Textstring + sectionLinks MultiURLPicker). 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, titre li.level-3), repris d'apol-website.
    • Cf. .claude/docs/architecture/frontend-build.md (Header PEL / Header APOL).

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é — DataType TruefalseDefaultTrue, comme sur directoryPage : 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 page directoryPage avec ?q=. Sans page directoryPage dans l'arbre, le champ de recherche est masqué. Vue blockgrid/Components/directoryBlock.cshtml -> partagée Views/Partials/blocks/_directory.cshtml : deux onglets Collaborateurs / Contacts externes (membres intranetMember via IMemberDirectoryService, contacts externes via IExternalContactService). Le même partial sert le template Views/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 »). Vue blockgrid/Components/birthdaysBlock.cshtml -> une personne = blocks/_birthdayPerson.cshtml (avatar via _memberAvatar.cshtml, âge via DirectoryRules.AgeOn).
  • dutyOfficerBlock (Permanence), clé b10c0027, groupe Modules dynamiques : title (Textstring, défaut « Permanence ») et dutyOfficer (Member Picker). Porté du widget felix WidgetPermanence : affiche l'officier de permanence et ses numéros en liens tel:, 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é via IMemberDirectoryService.GetByKey, qui ignore volontairement showInDirectory (la personne a été désignée explicitement) mais écarte un membre désactivé. Vue blockgrid/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é unique quote (RichtextEditor) → blocks/quoteBlock.cshtml rend <blockquote class="blockquote block--quote">. SCSS styles/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é sur buttonBlock (link SingleURLPicker, style = Data List - Button Style réutilisé → btn--primary/secondary/outline/link, icon Textstring) via la BlockList Custom - Bloc List - Button. Le groupe : align (Button Picker - Horizontal Align réutilisé, émet justify-content-*), merged (→ btn-group), stacked (→ btn-group-vertical). blocks/buttonGroupBlock.cshtml : conteneur d-flex flex-wrap @align + conteneur interne (btn-group/btn-group-vertical/d-flex gap-2). Aucun SCSS custom (Bootstrap + _button.scss existant).

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ées sliderItem (b10c0023-...-a0 : image, title, text RTE, link), via la BlockList Custom - 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'attribut data-splide (comme pel) ; blocks/sliderBlock.cshtml produit le markup .splide et scripts/modules/blocks/slider.js (lazy, sur .js-slider) monte Splide en lisant data-splide. SCSS styles/base/blocks/_slider.scss (mode slider--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 au widgetCard pel. Propriétés : title/titleSize (DropdownHeadingSizes), text (RTE), image et video (fond, MediaPicker3), link (carte cliquable = stretched-link), button (SingleURLPicker, au-dessus du lien via position-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 : mode below = image (au ratio) puis corps ; mode superposé = corps position-absolute aligné sur le cover. Vidéo de fond en autoplay muted loop (comme le cover de oneColumnLayoutBlock, pas de lecture au survol). SCSS styles/base/blocks/_card.scss (classes ratio-*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 : grid grille carrée par défaut ou masonry colonnes 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 Ratio ré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, composition pageSettings, aucune propriété propre. Ajouté à la Structure de homePage (un nœud par site, sous la home). N'autorise que announcementPage comme enfant.
  • announcementPage (Annonce) (a11ce000-...-02) : composition pageSettings. 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.mdVacationsEditor).

  • Champ vacations sur intranetMember (onglet « Vacances ») : DataType Vacances (EditorAlias Umbraco.TextArea, EditorUIAlias Spektrum.PropertyEditorUi.VacationsEditor, dossier Custom). Valeur = JSON maison [{ year, entries:[{ title, start, end, note }] }]. Organisation en dossiers par année (créés à la main), historique conservé.
  • Lecture : MemberDirectoryService.Map parse le JSON → DirectoryMember.Vacations (périodes aplaties, Year gardé). GetUpcomingVacations(take) = en cours/à venir ; GetAllVacations() = toutes (passé + futur, pour le calendrier). Voir extensibility.md.
  • Bloc vacationsBlock (Vacances) (b10c0028, groupe Modules dynamiques de CustomBlockGridsBase) : title, view (Data List calendar/timeline, défaut calendrier) et count (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 JS scripts/modules/blocks/vacations.js, données JSON inline, safelist vacations-popup). Timeline = rendu serveur réutilisant le visuel events-timeline (année/mois). Pas d'avatar. Vue blockgrid/Components/vacationsBlock.cshtml (typée VacationsBlock). PEL uniquement : alias dans PelBlocks (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 UI Spektrum.PropertyEditorUi.ButtonPicker (schema Umbraco.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 }), la defaultValue et l'option "boutons compacts" se configurent sur le DataType via l'editeur de config Spektrum.PropertyEditorUi.ButtonPickerConfig (repeater libelle / classe). 100 % frontend (aucun backend C#). Aucun DataType prelivre : creer un Data Type base 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 template upstream : 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 (WidgetDocument RetreiveFilesAndLinks). Le documentsBlock le rend comme un lien (icon-file-link, ouvre l'URL) au lieu d'un téléchargement. Compose felixMigration (pour l'import).
    • File porte documentTitle (TextBox) et documentDescription (TextArea) — titre/description affichables d'un document (repris de felix, utilisés par le documentsBlock).
    • Composition média temporaire felixMigration (clé f0000001-...-01, champ felixId Numeric) composée dans Image/File/Folder : garde l'ancien id felix pendant la migration de contenu (mapping ancien→nouveau). À supprimer en fin de migration. Voir migrations/felix/.
  • Composition contenu temporaire felixMigrationContent (clé f0000002-...-02, champ felixId) composée dans eventPage/eventFolder et articlePage/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) — champs firstName, 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 de showInDirectory : permet de figurer à l'annuaire sans exposer sa date de naissance ; comme birthDate/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 via IMemberDirectoryService. Porte aussi memberReadArticles (TextArea, CanEdit/CanView à false) : les ids des actualités déjà ouvertes, en CSV, tenus à jour par IArticleReadService — repris de felix, voir business-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 : marquee Change="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.

Contributors

No contributors

Changelog

No recent changes