26 Commits
Author SHA1 Message Date
bachir 6627a63805 project page title scale down on scroll 2026-08-17 12:32:16 +02:00
bachir b1ad60e76e added captions & lightbox to project page 2026-08-16 15:10:25 +02:00
bachir edec95abbf home double colonne vitesse différente, page projet empilement des images 2026-08-15 21:55:05 +02:00
bachir 9f9a2501ee started projet viewmode full page 2026-08-15 18:58:29 +02:00
bachir d3799fb3b3 ajouté cesures sur titres variables 2026-08-13 18:28:39 +02:00
bachir c19b5899e4 migration is cleaning textes, added pdf migration 2026-08-13 12:21:53 +02:00
bachir 914f5fd9d1 variables titles are ok 2026-08-13 11:58:27 +02:00
bachir 612896d2c6 header blocks + structure sync + menu nav principale 2026-07-26 16:58:34 +02:00
bachir 6a190effab migration: ignore les backups .back.md des textes source 2026-07-26 16:42:56 +02:00
bachir a2cb5f9dcf migration projets: body markdown, images HD, légendes, personnes prénom/nom
- ProjetNode: body Markdown->HTML (strip HTML brut), images + légendes lues
  depuis public://drive_shared_leshed/Page{UID}
- ImageResizeImport: nouveau process plugin, redimensionne les images en web HD
  (max 1920px) et crée le fichier managé (idempotent)
- field_images: caption via image_field_caption, alt depuis les légendes
- PersonneGenerate: nom dans field_nom (titre auto-généré via auto_entitylabel),
  dédup sur field_nom + field_prenom
- title: skip_on_empty (ligne ignorée si "Nom du projet" vide)
- CER: relation curateur sur field_projets_curates (distinct de field_projet_s_lie_s)
- deps: + league/commonmark, image_field_caption, auto_entitylabel ; - migrate_file
2026-07-26 16:26:18 +02:00
bachir 829653a9e9 cleaned font folder 2026-07-17 16:46:25 +02:00
bachir 0e1b0158d7 started titles splitted textes 2026-07-17 16:45:27 +02:00
bachir 8bf17caf19 layour and burger menu 2026-07-17 16:18:39 +02:00
bachir 45458cdbb8 added epilogue font 2026-07-17 15:39:01 +02:00
bachir 30ca6320f4 added missing templates 2026-07-17 15:38:46 +02:00
bachir fc621923d1 builded for prod theme assets 2026-07-13 18:12:48 +02:00
bachir c8a0fbf28b activated shield 2026-07-13 18:01:40 +02:00
bachir deb616a5d1 config ignored migrate_google_sheets settings 2026-07-13 17:56:21 +02:00
bachir 613b27af03 removed google api key from source code 2026-07-13 16:25:19 +02:00
bachir 43113e662b started druapl theme leshed 2026-07-13 16:24:52 +02:00
bachir 688715e47f removed google api key from source code 2026-07-12 19:01:27 +02:00
bachir 47a4f04c65 more migration 2026-07-12 18:55:08 +02:00
bachir dfefc56268 fixed date field migration on projet_node 2026-07-11 16:39:46 +02:00
bachir a2a0e423a3 added 'invité/accompagné/curaté par' field to migration, with CT personne node creation 2026-07-11 16:06:12 +02:00
bachir ec5b13be3c started custom migration module migrate_leshed 2026-07-11 15:20:42 +02:00
bachir a1201aff21 updated profile 2026-07-10 12:33:54 +02:00
106 changed files with 16108 additions and 453 deletions
+25
View File
@@ -0,0 +1,25 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Listen for Xdebug",
"type": "php",
"request": "launch",
"port": 9001,
"pathMappings": {
"/var/www/leshed": "${workspaceFolder}"
},
},
{
"name": "Launch currently open script",
"type": "php",
"request": "launch",
"program": "${file}",
"cwd": "${fileDirname}",
"port": 9001
}
]
}
File diff suppressed because it is too large Load Diff
+631
View File
@@ -0,0 +1,631 @@
<!-- THEME DEBUG -->
<!-- THEME HOOK: 'html' -->
<!-- FILE NAME SUGGESTIONS:
▪️ html--node--570.html.twig
▪️ html--node--%.html.twig
▪️ html--node.html.twig
✅ html.html.twig
-->
<!-- 💡 BEGIN CUSTOM TEMPLATE OUTPUT from 'themes/custom/leshed/templates/html.html.twig' -->
<!DOCTYPE html>
<html lang="fr" dir="ltr">
<head>
<meta charset="utf-8" />
<meta name="Generator" content="Drupal 10 (https://www.drupal.org)" />
<meta name="MobileOptimized" content="width" />
<meta name="HandheldFriendly" content="true" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" href="/core/misc/favicon.ico" type="image/vnd.microsoft.icon" />
<link rel="alternate" hreflang="fr" href="http://dev.leshed.fr/node/570" />
<link rel="canonical" href="http://dev.leshed.fr/node/570" />
<link rel="shortlink" href="http://dev.leshed.fr/node/570" />
<title>FAGO | Le Shed</title>
<link rel="stylesheet" media="all" href="/core/modules/system/css/components/align.module.css?tjtjpd" />
<link rel="stylesheet" media="all" href="/core/modules/system/css/components/fieldgroup.module.css?tjtjpd" />
<link rel="stylesheet" media="all" href="/core/modules/system/css/components/container-inline.module.css?tjtjpd" />
<link rel="stylesheet" media="all" href="/core/modules/system/css/components/clearfix.module.css?tjtjpd" />
<link rel="stylesheet" media="all" href="/core/modules/system/css/components/details.module.css?tjtjpd" />
<link rel="stylesheet" media="all" href="/core/modules/system/css/components/hidden.module.css?tjtjpd" />
<link rel="stylesheet" media="all" href="/core/modules/system/css/components/item-list.module.css?tjtjpd" />
<link rel="stylesheet" media="all" href="/core/modules/system/css/components/js.module.css?tjtjpd" />
<link rel="stylesheet" media="all" href="/core/modules/system/css/components/nowrap.module.css?tjtjpd" />
<link rel="stylesheet" media="all" href="/core/modules/system/css/components/position-container.module.css?tjtjpd" />
<link rel="stylesheet" media="all" href="/core/modules/system/css/components/reset-appearance.module.css?tjtjpd" />
<link rel="stylesheet" media="all" href="/core/modules/system/css/components/resize.module.css?tjtjpd" />
<link rel="stylesheet" media="all" href="/core/modules/system/css/components/system-status-counter.css?tjtjpd" />
<link rel="stylesheet" media="all" href="/core/modules/system/css/components/system-status-report-counters.css?tjtjpd" />
<link rel="stylesheet" media="all" href="/core/modules/system/css/components/system-status-report-general-info.css?tjtjpd" />
<link rel="stylesheet" media="all" href="/core/modules/system/css/components/tablesort.module.css?tjtjpd" />
<link rel="stylesheet" media="all" href="/modules/contrib/extlink/css/extlink.css?tjtjpd" />
</head>
<body>
<a href="#main-content" class="visually-hidden focusable">
Aller au contenu principal
</a>
<!-- THEME DEBUG -->
<!-- THEME HOOK: 'off_canvas_page_wrapper' -->
<!-- BEGIN OUTPUT from 'core/modules/system/templates/off-canvas-page-wrapper.html.twig' -->
<div class="dialog-off-canvas-main-canvas" data-off-canvas-main-canvas>
<!-- THEME DEBUG -->
<!-- THEME HOOK: 'page' -->
<!-- FILE NAME SUGGESTIONS:
▪️ page--node--570.html.twig
▪️ page--node--%.html.twig
▪️ page--node.html.twig
✅ page.html.twig
-->
<!-- 💡 BEGIN CUSTOM TEMPLATE OUTPUT from 'themes/custom/leshed/templates/page.html.twig' -->
<div class="layout-container">
<header role="banner">
<section class="header-top"></section>
<section class="header-middle">
<section class="header-left">
<!-- THEME DEBUG -->
<!-- THEME HOOK: 'region' -->
<!-- FILE NAME SUGGESTIONS:
▪️ region--header-left.html.twig
✅ region.html.twig
-->
<!-- 💡 BEGIN CUSTOM TEMPLATE OUTPUT from 'themes/custom/leshed/templates/region.html.twig' -->
<div class="wrapper">
<!-- THEME DEBUG -->
<!-- THEME HOOK: 'block' -->
<!-- FILE NAME SUGGESTIONS:
▪️ block--leshed-identitedusite.html.twig
✅ block--system-branding-block.html.twig
▪️ block--system.html.twig
▪️ block.html.twig
-->
<!-- BEGIN OUTPUT from 'core/modules/system/templates/block--system-branding-block.html.twig' -->
<div id="block-leshed-identitedusite">
<a href="/" rel="home">Le Shed</a>
Centre d'art en Normandie
</div>
<!-- END OUTPUT from 'core/modules/system/templates/block--system-branding-block.html.twig' -->
</div>
<!-- END CUSTOM TEMPLATE OUTPUT from 'themes/custom/leshed/templates/region.html.twig' -->
</section>
<section class="header-center"></section>
<section class="header-right">
<!-- THEME DEBUG -->
<!-- THEME HOOK: 'region' -->
<!-- FILE NAME SUGGESTIONS:
✅ region--header-right.html.twig
▪️ region.html.twig
-->
<!-- 💡 BEGIN CUSTOM TEMPLATE OUTPUT from 'themes/custom/leshed/templates/region--header-right.html.twig' -->
<div id="burger-btn"></div>
<div class="wrapper">
<!-- THEME DEBUG -->
<!-- THEME HOOK: 'block' -->
<!-- FILE NAME SUGGESTIONS:
▪️ block--leshed-navigationprincipale.html.twig
▪️ block--menu-block--main.html.twig
▪️ block--menu-block.html.twig
✅ block--system-menu-block.html.twig
▪️ block.html.twig
-->
<!-- BEGIN OUTPUT from 'core/modules/system/templates/block--system-menu-block.html.twig' -->
<nav role="navigation" aria-labelledby="block-leshed-navigationprincipale-menu" id="block-leshed-navigationprincipale">
<h2 class="visually-hidden" id="block-leshed-navigationprincipale-menu">Navigation principale</h2>
<!-- THEME DEBUG -->
<!-- THEME HOOK: 'menu' -->
<!-- FILE NAME SUGGESTIONS:
▪️ menu--main.html.twig
✅ menu.html.twig
-->
<!-- BEGIN OUTPUT from 'core/modules/system/templates/menu.html.twig' -->
<ul>
<li>
<a href="/" data-drupal-link-system-path="&lt;front&gt;">Accueil</a>
</li>
</ul>
<!-- END OUTPUT from 'core/modules/system/templates/menu.html.twig' -->
</nav>
<!-- END OUTPUT from 'core/modules/system/templates/block--system-menu-block.html.twig' -->
</div>
<!-- END CUSTOM TEMPLATE OUTPUT from 'themes/custom/leshed/templates/region--header-right.html.twig' -->
</section>
</section>
<section class="header-bottom"></section>
</header>
<nav role="breadcrumb">
</nav>
<main role="main">
<a id="main-content" tabindex="-1"></a>
<div class="layout-content">
<!-- THEME DEBUG -->
<!-- THEME HOOK: 'region' -->
<!-- FILE NAME SUGGESTIONS:
▪️ region--content.html.twig
✅ region.html.twig
-->
<!-- 💡 BEGIN CUSTOM TEMPLATE OUTPUT from 'themes/custom/leshed/templates/region.html.twig' -->
<div class="wrapper">
<div data-drupal-messages-fallback class="hidden"></div>
<!-- THEME DEBUG -->
<!-- THEME HOOK: 'node' -->
<!-- FILE NAME SUGGESTIONS:
▪️ node--570--full.html.twig
▪️ node--570.html.twig
✅ node--projet--full.html.twig
▪️ node--projet.html.twig
▪️ node--full.html.twig
▪️ node.html.twig
-->
<!-- 💡 BEGIN CUSTOM TEMPLATE OUTPUT from 'themes/custom/leshed/templates/node--projet--full.html.twig' -->
<article class="node-type-projet view-mode-full">
<div class="cols">
<div class="col left">
<h2>
<!-- THEME DEBUG -->
<!-- THEME HOOK: 'field' -->
<!-- FILE NAME SUGGESTIONS:
▪️ field--node--title--projet.html.twig
✅ field--node--title.html.twig
▪️ field--node--projet.html.twig
▪️ field--title.html.twig
▪️ field--string.html.twig
▪️ field.html.twig
-->
<!-- BEGIN OUTPUT from 'core/modules/node/templates/field--node--title.html.twig' -->
<span class="title">FAGO</span>
<!-- END OUTPUT from 'core/modules/node/templates/field--node--title.html.twig' -->
</h2>
<!-- THEME DEBUG -->
<!-- THEME HOOK: 'field' -->
<!-- FILE NAME SUGGESTIONS:
▪️ field--node--field-artiste-s-invite-s--projet.html.twig
▪️ field--node--field-artiste-s-invite-s.html.twig
▪️ field--node--projet.html.twig
▪️ field--field-artiste-s-invite-s.html.twig
▪️ field--entity-reference.html.twig
✅ field.html.twig
-->
<!-- 💡 BEGIN CUSTOM TEMPLATE OUTPUT from 'themes/custom/leshed/templates/field.html.twig' -->
<div class="field_artiste_s_invite_s field-items multiple">
<div class="field-item"><a href="/node/569" hreflang="fr">Géraldine Longueville</a></div>
</div>
<!-- END CUSTOM TEMPLATE OUTPUT from 'themes/custom/leshed/templates/field.html.twig' -->
<!-- THEME DEBUG -->
<!-- THEME HOOK: 'field' -->
<!-- FILE NAME SUGGESTIONS:
▪️ field--node--field-invite-accompagne-curate-p--projet.html.twig
▪️ field--node--field-invite-accompagne-curate-p.html.twig
▪️ field--node--projet.html.twig
▪️ field--field-invite-accompagne-curate-p.html.twig
▪️ field--entity-reference.html.twig
✅ field.html.twig
-->
<!-- 💡 BEGIN CUSTOM TEMPLATE OUTPUT from 'themes/custom/leshed/templates/field.html.twig' -->
<div class="field_invite_accompagne_curate_p">
<div class="field-label">invite/accompagné/curaté par</div>
<div class="field-items multiple">
<div class="field-item"><a href="/node/508" hreflang="fr">Virginie Bobin</a></div>
</div>
</div>
<!-- END CUSTOM TEMPLATE OUTPUT from 'themes/custom/leshed/templates/field.html.twig' -->
<!-- THEME DEBUG -->
<!-- THEME HOOK: 'field' -->
<!-- FILE NAME SUGGESTIONS:
▪️ field--node--field-dates--projet.html.twig
▪️ field--node--field-dates.html.twig
▪️ field--node--projet.html.twig
▪️ field--field-dates.html.twig
▪️ field--daterange.html.twig
✅ field.html.twig
-->
<!-- 💡 BEGIN CUSTOM TEMPLATE OUTPUT from 'themes/custom/leshed/templates/field.html.twig' -->
<div class="field_dates field-item">
<!-- THEME DEBUG -->
<!-- THEME HOOK: 'time' -->
<!-- BEGIN OUTPUT from 'core/modules/system/templates/time.html.twig' -->
<time datetime="2026-06-28T12:00:00Z">28 juin 2026</time>
<!-- END OUTPUT from 'core/modules/system/templates/time.html.twig' -->
-
<!-- THEME DEBUG -->
<!-- THEME HOOK: 'time' -->
<!-- BEGIN OUTPUT from 'core/modules/system/templates/time.html.twig' -->
<time datetime="2026-09-27T12:00:00Z">27 Sep 2026</time>
<!-- END OUTPUT from 'core/modules/system/templates/time.html.twig' -->
</div>
<!-- END CUSTOM TEMPLATE OUTPUT from 'themes/custom/leshed/templates/field.html.twig' -->
<!-- THEME DEBUG -->
<!-- THEME HOOK: 'field' -->
<!-- FILE NAME SUGGESTIONS:
▪️ field--node--body--projet.html.twig
▪️ field--node--body.html.twig
▪️ field--node--projet.html.twig
▪️ field--body.html.twig
▪️ field--text-with-summary.html.twig
✅ field.html.twig
-->
<!-- 💡 BEGIN CUSTOM TEMPLATE OUTPUT from 'themes/custom/leshed/templates/field.html.twig' -->
<div class="body field-item"><p>De lAlgérie à la Normandie en passant par le Japon, lartiste et cueilleuse Géraldine Longueville retrace les trajectoires entremêlées de plantes et de corps pris dans les mailles de récits intimes et politiques, notamment liés à lhistoire coloniale. Spécialement produites pour lexposition, les œuvres présentées (installations, film, pièce sonore) composent un jardin à la fois mémoriel et fabuleux, fruit de gestes, de processus et dhistoires transmises par lartiste lors dateliers et de performances. Cueillies, séchées, nouées, consumées ou macérées, les plantes déposées dans les pièces de La maison accueillant le Shed à Maromme en appellent à tous nos sens. Entre amertume et guérison, elles invitent à se rassembler autour delles le temps dune boisson ou dune chanson.</p>
<p><em>Fago</em> est lultime étape des recherches menées par Géraldine Longueville dans le cadre du programme doctoral RADIAN (ésam Caen-Cherbourg, ésadhar, ENSA Normandie et Normandie-Université).</p>
<p>Une exposition curatée par Virginie Bobin Dans le cadre de Normandie Impressionniste</p>
<p>Assistant artistique : Alioun Abderrahmane</p>
<h1>Géraldine Longueville</h1>
<p>Diplômée dun Master Fine Arts de Sandberg Institute à Amsterdam et dun Master Histoire de lArt de la Sorbonne Université Paris, elle poursuit actuellement une thèse en Recherche et Créationu sein du programme Radian (ésam Caen-Cherbourg, ésadhar, ENSA Normandie et Normandie-Université). Elle a travaillé dans des centres dart contemporains franciliens (Emba Manet Gennevilliers, La Galerie Noisy-le-Sec) et à l’école nationale darts Paris-Cergy (ENSAPC). Depuis 2016, elle enseigne la performance et l’écriture à lEcole Européenne Supérieure de lImage de Poitiers (EESI).</p>
<p>Sa pratique artistique est transversale, mêlant écriture, chant, musique, comestible et installation. Elle se déploie à travers différentes formes de collaborations et de partage tant avec les artistes que le public. La circulation des savoirs écologiques et leurs transmissions sensorielles sur site sont au cœur de ses productions. En 2019, elle se forme aux jardins des plantes médicinales au Potager du Roi à l'École nationale supérieure du paysage à Versailles et elle suit la formation du cueilleur.euse.s Le chemin de la nature de Christophe de Hody.<br>
Pendant une vingtaine d'années, elle a mené de nombreux projets collectifs : membre de Glassbox en 2002, elle a monté la galerie extérieure en 2005, projet d'intervention artistique dans lespace public à Paris et aux Etats-Unis. En 2011, avec les artistes David Bersntein et Jurgis Paskevicius, elle crée le trio Jugedamos avec lequel elle réalise de nombreuses performances à Amsterdam (De Appel), New-York (Sculpture Center) ou Rome (Nomas Foundation). De retour à Paris en 2014, avec la cheffe Virginie Galan et les designers graphiques Commune, elle cofonde Black Garlic, un atelier en art et gastronomie qui organise des dîners dans des brasseries et des lieux darts.<br>
Depuis 2017, elle travaille sur des projets personnels incluants des invitations collaboratives qui sont exposés au Cac Bretigny, Bétonsalon Paris, galerie gb agency Paris, Goethe Institut Nancy, Kyoto art center, galerie Jocelyn Wolff Paris et la Grande Galerie de l’ésam de Caen.<br>
Elle mène depuis 2019 une recherche sur lamertume, goût et sentiment qui selon elle traduisent la persistance du colonialisme et la résistance à la colonialité. Elle travaille actuellement sur les différentes modalités dexposition de cette recherche qui seront présentées à partir de 2026.</p>
<h1>Virginie Bobin</h1>
<p>Virginie Bobin travaille de manière indépendante et souvent collective au croisement des pratiques curatoriales et éditoriales, de l'écriture, de la pédagogie et de la traduction. Après avoir œuvré pendant près de dix ans au sein de centres d'art et lieux de résidence en France et à l'international (Bétonsalon - Centre d'art et de recherche, la Villa Vassilieff, le Witte de With, Les Laboratoires d'Aubervilliers, Performa), elle s'efforce aujourd'hui d'instaurer des pratiques collectives d'apprentissage, d'écoute et de soin dans différents contextes, en s'appuyant sur des méthodologies féministes.<br>
En 2024, elle rejoint l'ésadhar de Rouen en tant que Professeure en Art et Pratiques sociales. Docteure en recherche artistique (PhD-in-Practice de lAcadémie des Beaux-Arts de Vienne, 2023), elle est membre co-fondatrice de la plateforme Qalqalah قلقلة (<a href="http://www.qalqalah.org">www.qalqalah.org</a>), avec laquelle elle a mené des projets à la Ferme du Buisson, au CRAC-Occitanie à Sète, à La Kunsthalle Mulhouse, au Tanzquartier (Vienne), à Buda (Courtrai), la Fondazione Pistoletto (Biella), l'Atelier de l'Observatoire (Casablanca) ou encore au Master exerce à l'ICI-CCN (Montpellier). Traductrice, elle collabore notamment avec le magazine The Funambulist.<br>
Ses projets curatoriaux et de recherche ont été présentés dans des institutions françaises et internationales telles que le CAC Brétigny, Bétonsalon - Centre d'art et de recherche, Museion (Bolzano), CentroCentro (Madrid), MoMA PS1 et e-flux space (New York) ou Tabakalera (San Sebastian). Elle a dirigé plusieurs ouvrages collectifs : Bestiario de Lengüitas (fruit d'une collaboration au long cours avec l'artiste Mercedes Azpilicueta, k.verlag, 2024), Composing Differences (Les Presses du Réel, 2015) et Re-publications (en collaboration avec Mathilde Villeneuve, Archive Books, 2015). Sont parus en 2025 : Désapprendre en traduisant : une pratique critique et collective (Villa Arson / Sternberg Press) et L'interprète dis/paraît (Scriptings).<br>
Vivant dans une commune rurale de Normandie, Virginie Bobin est membre des conseils d'administration du réseau RN13BIS et de l'association Kit de Survie.</p>
<h1>Fago</h1>
<p><strong>Ecouter</strong> des récits déracinés...<br>
<strong>Goûter</strong> une boisson d'accueil...<br>
<strong>Humer</strong> un paysage en mouvement...<br>
<strong>Toucher</strong> des gestes de réparation...<br>
<strong>Percevoir</strong> des jeux d'ombre et de lumière...</p>
</div>
<!-- END CUSTOM TEMPLATE OUTPUT from 'themes/custom/leshed/templates/field.html.twig' -->
<!-- THEME DEBUG -->
<!-- THEME HOOK: 'field' -->
<!-- FILE NAME SUGGESTIONS:
▪️ field--node--field-partenaire-s--projet.html.twig
▪️ field--node--field-partenaire-s.html.twig
▪️ field--node--projet.html.twig
▪️ field--field-partenaire-s.html.twig
▪️ field--entity-reference.html.twig
✅ field.html.twig
-->
<!-- 💡 BEGIN CUSTOM TEMPLATE OUTPUT from 'themes/custom/leshed/templates/field.html.twig' -->
<div class="field_partenaire_s">
<div class="field-label">Partenaire(s) Associé(s)</div>
<div class="field-items multiple">
<div class="field-item"><a href="/node/57" hreflang="fr">Normandie Impressionniste</a></div>
</div>
</div>
<!-- END CUSTOM TEMPLATE OUTPUT from 'themes/custom/leshed/templates/field.html.twig' -->
</div>
<div class="col right">
<!-- THEME DEBUG -->
<!-- THEME HOOK: 'field' -->
<!-- FILE NAME SUGGESTIONS:
▪️ field--node--field-images--projet.html.twig
▪️ field--node--field-images.html.twig
▪️ field--node--projet.html.twig
▪️ field--field-images.html.twig
▪️ field--image.html.twig
✅ field.html.twig
-->
<!-- 💡 BEGIN CUSTOM TEMPLATE OUTPUT from 'themes/custom/leshed/templates/field.html.twig' -->
<div class="field_images field-items multiple">
<div class="field-item">
<!-- THEME DEBUG -->
<!-- THEME HOOK: 'image_formatter' -->
<!-- BEGIN OUTPUT from 'core/modules/image/templates/image-formatter.html.twig' -->
<!-- THEME DEBUG -->
<!-- THEME HOOK: 'image' -->
<!-- BEGIN OUTPUT from 'core/modules/system/templates/image.html.twig' -->
<img loading="lazy" src="/sites/default/files/projets/103/103_IMG917.jpg" width="1920" height="1419" alt="Fago, Géraldine Longueville, 2026, sculpture en bambou, organza,treuil mécanique, corse, plantes. Crédits photographiques : © Marc Domage" />
<!-- END OUTPUT from 'core/modules/system/templates/image.html.twig' -->
<!-- END OUTPUT from 'core/modules/image/templates/image-formatter.html.twig' -->
</div>
<div class="field-item">
<!-- THEME DEBUG -->
<!-- THEME HOOK: 'image_formatter' -->
<!-- BEGIN OUTPUT from 'core/modules/image/templates/image-formatter.html.twig' -->
<!-- THEME DEBUG -->
<!-- THEME HOOK: 'image' -->
<!-- BEGIN OUTPUT from 'core/modules/system/templates/image.html.twig' -->
<img loading="lazy" src="/sites/default/files/projets/103/103_IMG1715.jpg" width="1920" height="1280" alt="Sharab, Géraldine Longueville, 2026, installation, mobilier, boisson d&#039;accueil, céramiques de Nina Safaïnia, bouteilles en grès, cuir. Crédits photographiques : © Marc Domage" />
<!-- END OUTPUT from 'core/modules/system/templates/image.html.twig' -->
<!-- END OUTPUT from 'core/modules/image/templates/image-formatter.html.twig' -->
</div>
<div class="field-item">
<!-- THEME DEBUG -->
<!-- THEME HOOK: 'image_formatter' -->
<!-- BEGIN OUTPUT from 'core/modules/image/templates/image-formatter.html.twig' -->
<!-- THEME DEBUG -->
<!-- THEME HOOK: 'image' -->
<!-- BEGIN OUTPUT from 'core/modules/system/templates/image.html.twig' -->
<img loading="lazy" src="/sites/default/files/projets/103/103_IMG1729.jpg" width="1280" height="1920" alt="Sharab, Géraldine Longueville, 2026, installation, mobilier, boisson d&#039;accueil, céramiques de Nina Safaïnia, bouteilles en grès, cuir. Performance de service réalisée par Morane Remaud. Crédits photographiques : © Marc Domage" />
<!-- END OUTPUT from 'core/modules/system/templates/image.html.twig' -->
<!-- END OUTPUT from 'core/modules/image/templates/image-formatter.html.twig' -->
</div>
<div class="field-item">
<!-- THEME DEBUG -->
<!-- THEME HOOK: 'image_formatter' -->
<!-- BEGIN OUTPUT from 'core/modules/image/templates/image-formatter.html.twig' -->
<!-- THEME DEBUG -->
<!-- THEME HOOK: 'image' -->
<!-- BEGIN OUTPUT from 'core/modules/system/templates/image.html.twig' -->
<img loading="lazy" src="/sites/default/files/projets/103/103_IMG2010.jpg" width="1920" height="1221" alt="Fago, Géraldine Longueville, 2026, sculpture en bambou, organza, treuil mécanique, corde, plantes. Crédits photographiques : © Marc Domage" />
<!-- END OUTPUT from 'core/modules/system/templates/image.html.twig' -->
<!-- END OUTPUT from 'core/modules/image/templates/image-formatter.html.twig' -->
</div>
<div class="field-item">
<!-- THEME DEBUG -->
<!-- THEME HOOK: 'image_formatter' -->
<!-- BEGIN OUTPUT from 'core/modules/image/templates/image-formatter.html.twig' -->
<!-- THEME DEBUG -->
<!-- THEME HOOK: 'image' -->
<!-- BEGIN OUTPUT from 'core/modules/system/templates/image.html.twig' -->
<img loading="lazy" src="/sites/default/files/projets/103/103_IMG2025.jpg" width="1920" height="1280" alt="Fago, Géraldine Longueville, 2026, sculpture en bambou, organza, treuil mécanique, corde, plantes. Crédits photographiques : © Marc Domage" />
<!-- END OUTPUT from 'core/modules/system/templates/image.html.twig' -->
<!-- END OUTPUT from 'core/modules/image/templates/image-formatter.html.twig' -->
</div>
<div class="field-item">
<!-- THEME DEBUG -->
<!-- THEME HOOK: 'image_formatter' -->
<!-- BEGIN OUTPUT from 'core/modules/image/templates/image-formatter.html.twig' -->
<!-- THEME DEBUG -->
<!-- THEME HOOK: 'image' -->
<!-- BEGIN OUTPUT from 'core/modules/system/templates/image.html.twig' -->
<img loading="lazy" src="/sites/default/files/projets/103/103_IMG2051.jpg" width="1920" height="1280" alt="Fago, Géraldine Longueville, 2026, sculpture en bambou, organza, treuil mécanique, corde, plantes. Crédits photographiques : © Marc Domage" />
<!-- END OUTPUT from 'core/modules/system/templates/image.html.twig' -->
<!-- END OUTPUT from 'core/modules/image/templates/image-formatter.html.twig' -->
</div>
<div class="field-item">
<!-- THEME DEBUG -->
<!-- THEME HOOK: 'image_formatter' -->
<!-- BEGIN OUTPUT from 'core/modules/image/templates/image-formatter.html.twig' -->
<!-- THEME DEBUG -->
<!-- THEME HOOK: 'image' -->
<!-- BEGIN OUTPUT from 'core/modules/system/templates/image.html.twig' -->
<img loading="lazy" src="/sites/default/files/projets/103/103_IMG2064.jpg" width="1920" height="1391" alt="Vous la rappelez-vous ?, Géraldine Longueville, 2026, installation en bambou, numérisation de cartes postales imprimées sur papier, aquarelle. Crédits photographiques : © Marc Domage" />
<!-- END OUTPUT from 'core/modules/system/templates/image.html.twig' -->
<!-- END OUTPUT from 'core/modules/image/templates/image-formatter.html.twig' -->
</div>
<div class="field-item">
<!-- THEME DEBUG -->
<!-- THEME HOOK: 'image_formatter' -->
<!-- BEGIN OUTPUT from 'core/modules/image/templates/image-formatter.html.twig' -->
<!-- THEME DEBUG -->
<!-- THEME HOOK: 'image' -->
<!-- BEGIN OUTPUT from 'core/modules/system/templates/image.html.twig' -->
<img loading="lazy" src="/sites/default/files/projets/103/103_IMG2070.jpg" width="1920" height="1280" alt="kinakina, Géraldine Longueville, 2024, vidéo (durée : 25min), images, composition, voix : Géraldine Longueville ; Darbouka ; Olivier Longueville ; Dobro : John Molan et Elie Godard ; Arrangement sonore et mixage : Thomas Schmahl. Crédits photographiques : © Marc Domage" />
<!-- END OUTPUT from 'core/modules/system/templates/image.html.twig' -->
<!-- END OUTPUT from 'core/modules/image/templates/image-formatter.html.twig' -->
</div>
<div class="field-item">
<!-- THEME DEBUG -->
<!-- THEME HOOK: 'image_formatter' -->
<!-- BEGIN OUTPUT from 'core/modules/image/templates/image-formatter.html.twig' -->
<!-- THEME DEBUG -->
<!-- THEME HOOK: 'image' -->
<!-- BEGIN OUTPUT from 'core/modules/system/templates/image.html.twig' -->
<img loading="lazy" src="/sites/default/files/projets/103/103_IMG2075.jpg" width="1280" height="1920" alt="Fuma, Géraldine Longueville, 2026, performance et sculpture de plantes consumées, collier à vis. Crédits photographiques : © Marc Domage" />
<!-- END OUTPUT from 'core/modules/system/templates/image.html.twig' -->
<!-- END OUTPUT from 'core/modules/image/templates/image-formatter.html.twig' -->
</div>
<div class="field-item">
<!-- THEME DEBUG -->
<!-- THEME HOOK: 'image_formatter' -->
<!-- BEGIN OUTPUT from 'core/modules/image/templates/image-formatter.html.twig' -->
<!-- THEME DEBUG -->
<!-- THEME HOOK: 'image' -->
<!-- BEGIN OUTPUT from 'core/modules/system/templates/image.html.twig' -->
<img loading="lazy" src="/sites/default/files/projets/103/103_IMG2088.jpg" width="1920" height="1280" alt="Vous la rappelez-vous ?, Géraldine Longueville, 2026, installation en bambou, numérisation de cartes postales imprimées sur papier, aquarelle. Crédits photographiques : © Marc Domage" />
<!-- END OUTPUT from 'core/modules/system/templates/image.html.twig' -->
<!-- END OUTPUT from 'core/modules/image/templates/image-formatter.html.twig' -->
</div>
</div>
<!-- END CUSTOM TEMPLATE OUTPUT from 'themes/custom/leshed/templates/field.html.twig' -->
</div>
</div>
</article>
<!-- END CUSTOM TEMPLATE OUTPUT from 'themes/custom/leshed/templates/node--projet--full.html.twig' -->
</div>
<!-- END CUSTOM TEMPLATE OUTPUT from 'themes/custom/leshed/templates/region.html.twig' -->
</div>
</main>
</div>
<!-- END CUSTOM TEMPLATE OUTPUT from 'themes/custom/leshed/templates/page.html.twig' -->
</div>
<!-- END OUTPUT from 'core/modules/system/templates/off-canvas-page-wrapper.html.twig' -->
<script type="application/json" data-drupal-selector="drupal-settings-json">{"path":{"baseUrl":"\/","pathPrefix":"","currentPath":"node\/570","currentPathIsAdmin":false,"isFront":false,"currentLanguage":"fr"},"pluralDelimiter":"\u0003","suppressDeprecationErrors":true,"data":{"extlink":{"extTarget":false,"extTargetAppendNewWindowDisplay":false,"extTargetAppendNewWindowLabel":"(opens in a new window)","extTargetNoOverride":false,"extNofollow":false,"extTitleNoOverride":false,"extNoreferrer":false,"extFollowNoOverride":false,"extClass":"ext","extLabel":"(le lien est externe)","extImgClass":false,"extSubdomains":true,"extExclude":"","extInclude":"","extCssExclude":"","extCssInclude":"","extCssExplicit":"","extAlert":false,"extAlertText":"Ce lien vous dirige vers un site externe. Nous ne sommes pas responsables de son contenu.","extHideIcons":false,"mailtoClass":"mailto","telClass":"tel","mailtoLabel":"(link sends email)","telLabel":"(link is a phone number)","extUseFontAwesome":false,"extIconPlacement":"append","extPreventOrphan":false,"extFaLinkClasses":"fa fa-external-link","extFaMailtoClasses":"fa fa-envelope-o","extAdditionalLinkClasses":"","extAdditionalMailtoClasses":"","extAdditionalTelClasses":"","extFaTelClasses":"fa fa-phone","allowedDomains":null,"extExcludeNoreferrer":""}},"user":{"uid":0,"permissionsHash":"ec925e94d2171db34f4a2e24795940b32ebbfae3c8882b3c70d7f1931d114517"}}</script>
<script src="/sites/default/files/languages/fr_CrdT4JHfBTSbUhzO9RLz5imL3dFblqGqJOpzKqtwZGA.js?tjtjpd"></script>
<script src="/core/misc/drupalSettingsLoader.js?v=10.6.10"></script>
<script src="/core/misc/drupal.js?v=10.6.10"></script>
<script src="/core/misc/drupal.init.js?v=10.6.10"></script>
<script src="/modules/contrib/extlink/js/extlink.js?tjtjpd"></script>
<script src="http://localhost:5173/@vite/client" type="module"></script>
<script src="http://localhost:5173/assets/js/main.js" type="module"></script>
</body>
</html>
<!-- END CUSTOM TEMPLATE OUTPUT from 'themes/custom/leshed/templates/html.html.twig' -->
+10
View File
@@ -16,10 +16,20 @@
], ],
"require": { "require": {
"composer/installers": "^2.0", "composer/installers": "^2.0",
"drupal/auto_entitylabel": "^3.4",
"drupal/cer": "^5.0@beta", "drupal/cer": "^5.0@beta",
"drupal/config_ignore": "^3.4",
"drupal/core-composer-scaffold": "^10.6", "drupal/core-composer-scaffold": "^10.6",
"drupal/core-project-message": "^10.6", "drupal/core-project-message": "^10.6",
"drupal/core-recommended": "^10.6", "drupal/core-recommended": "^10.6",
"drupal/image_delta_formatter": "^1.3",
"drupal/image_field_caption": "^3.0",
"drupal/migrate_google_sheets": "^2.3",
"drupal/migrate_tools": "^6.1",
"drupal/page_manager": "^4.0@RC",
"drupal/shield": "^1.8",
"drupal/structure_sync": "^2.0",
"league/commonmark": "^2.8",
"wikimedia/composer-merge-plugin": "^2.1" "wikimedia/composer-merge-plugin": "^2.1"
}, },
"conflict": { "conflict": {
Generated
+1284 -246
View File
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,10 @@
status: 1
pattern: '[node:field_prenom] [node:field_nom]'
escape: false
preserve_titles: false
save: true
chunk: 50
dependencies:
config:
- node.type.personne
new_content_behavior: 0
@@ -0,0 +1,23 @@
uuid: e2f0e1c7-b805-4c1d-8988-760ebfe8fa7d
langcode: fr
status: true
dependencies:
module:
- system
theme:
- leshed
id: leshed_identitedusite
theme: leshed
region: header_left
weight: 0
provider: null
plugin: system_branding_block
settings:
id: system_branding_block
label: 'Identité du site'
label_display: '0'
provider: system
use_site_logo: false
use_site_name: true
use_site_slogan: true
visibility: { }
@@ -0,0 +1,34 @@
uuid: 372f96d2-3992-45b6-9335-b1359d3382e2
langcode: fr
status: true
dependencies:
config:
- system.menu.main
module:
- menu_block
theme:
- leshed
id: leshed_navigationprincipale
theme: leshed
region: header_right
weight: 0
provider: null
plugin: 'menu_block:main'
settings:
id: 'menu_block:main'
label: 'Navigation principale'
label_display: '0'
provider: menu_block
follow: false
follow_parent: child
display_empty: false
label_link: false
label_type: block
level: 1
depth: 0
expand_all_items: false
parent: 'main:'
render_parent: false
suggestion: main
hide_on_nonactive: false
visibility: { }
+29
View File
@@ -0,0 +1,29 @@
uuid: 013d1fb2-1c81-4b8b-93ca-0da2e401fd88
langcode: fr
status: false
dependencies:
module:
- ctools_block
theme:
- leshed
id: leshed_titre
theme: leshed
region: content
weight: 0
provider: null
plugin: 'entity_field:node:title'
settings:
id: 'entity_field:node:title'
label: Titre
label_display: '0'
provider: ctools_block
context_mapping:
entity: '@node.node_route_context:node'
formatter:
type: string
weight: 0
label: above
settings:
link_to_entity: false
third_party_settings: { }
visibility: { }
@@ -0,0 +1,14 @@
uuid: 143a87e9-a828-419c-8f07-117346183d44
langcode: fr
status: true
dependencies: { }
id: projet_artiste
label: 'Projet - Artiste'
enabled: true
first_field: field_artiste_s_invite_s
second_field: field_projet_s_lie_s
add_direction: append
bundles:
node:
- personne
- projet
@@ -0,0 +1,14 @@
uuid: 73bfbc5b-f77c-47d8-9725-aac43032587d
langcode: fr
status: true
dependencies: { }
id: projet_curateur
label: 'Projet - Curateur'
enabled: true
first_field: field_invite_accompagne_curate_p
second_field: field_projets_curates
add_direction: append
bundles:
node:
- personne
- projet
@@ -0,0 +1,14 @@
uuid: 6d877a10-29bf-41ad-8ed9-46a333e96e18
langcode: fr
status: true
dependencies: { }
id: projet_partenaire
label: 'Projet - Partenaire'
enabled: true
first_field: field_partenaire_s
second_field: field_projet_s_lie_s
add_direction: append
bundles:
node:
- partenaire
- projet
@@ -11,4 +11,6 @@ add_direction: append
bundles: bundles:
node: node:
- evenement - evenement
- partenaire
- personne
- projet - projet
+3 -1
View File
@@ -1,4 +1,6 @@
_core: _core:
default_config_hash: LzdDtE3Im_Ysio7g1Eo8ymSMq0mgSQMPzv0khsr_J64 default_config_hash: LzdDtE3Im_Ysio7g1Eo8ymSMq0mgSQMPzv0khsr_J64
langcode: fr langcode: fr
ignored_config_entities: { } mode: simple
ignored_config_entities:
- migrate_google_sheets.settings
@@ -8,7 +8,7 @@ id: node.personne.title
field_name: title field_name: title
entity_type: node entity_type: node
bundle: personne bundle: personne
label: Nom label: Title
description: '' description: ''
required: true required: true
translatable: true translatable: true
+8
View File
@@ -0,0 +1,8 @@
uuid: 177f93c8-cb82-4d48-a9f0-ba1f88df350b
langcode: fr
status: true
dependencies: { }
id: d_m_y
label: 'd M Y'
locked: false
pattern: 'd M Y'
@@ -162,4 +162,9 @@ content:
size: 60 size: 60
placeholder: '' placeholder: ''
third_party_settings: { } third_party_settings: { }
url_redirects:
weight: 50
region: content
settings: { }
third_party_settings: { }
hidden: { } hidden: { }
@@ -89,4 +89,9 @@ content:
size: 60 size: 60
placeholder: '' placeholder: ''
third_party_settings: { } third_party_settings: { }
url_redirects:
weight: 50
region: content
settings: { }
third_party_settings: { }
hidden: { } hidden: { }
@@ -6,13 +6,38 @@ dependencies:
- field.field.node.partenaire.body - field.field.node.partenaire.body
- field.field.node.partenaire.field_lien - field.field.node.partenaire.field_lien
- field.field.node.partenaire.field_logo - field.field.node.partenaire.field_logo
- field.field.node.partenaire.field_projet_s_lie_s
- image.style.thumbnail - image.style.thumbnail
- node.type.partenaire - node.type.partenaire
module: module:
- field_group
- image - image
- link - link
- path - path
- text - text
third_party_settings:
field_group:
group_details:
children:
- field_logo
- field_lien
- status
- path
- langcode
label: Details
region: content
parent_name: ''
weight: 3
format_type: details_sidebar
format_settings:
classes: ''
show_empty_fields: false
id: ''
label_as_html: false
open: true
description: ''
required_fields: true
weight: -100
id: node.partenaire.default id: node.partenaire.default
targetEntityType: node targetEntityType: node
bundle: partenaire bundle: partenaire
@@ -20,7 +45,7 @@ mode: default
content: content:
body: body:
type: text_textarea_with_summary type: text_textarea_with_summary
weight: 121 weight: 1
region: content region: content
settings: settings:
rows: 9 rows: 9
@@ -28,15 +53,9 @@ content:
placeholder: '' placeholder: ''
show_summary: false show_summary: false
third_party_settings: { } third_party_settings: { }
created:
type: datetime_timestamp
weight: 10
region: content
settings: { }
third_party_settings: { }
field_lien: field_lien:
type: link_default type: link_default
weight: 122 weight: 3
region: content region: content
settings: settings:
placeholder_url: '' placeholder_url: ''
@@ -44,57 +63,15 @@ content:
third_party_settings: { } third_party_settings: { }
field_logo: field_logo:
type: image_image type: image_image
weight: 123 weight: 2
region: content region: content
settings: settings:
progress_indicator: throbber progress_indicator: throbber
preview_image_style: thumbnail preview_image_style: thumbnail
third_party_settings: { } third_party_settings: { }
langcode: field_projet_s_lie_s:
type: language_select
weight: 2
region: content
settings:
include_locked: true
third_party_settings: { }
path:
type: path
weight: 30
region: content
settings: { }
third_party_settings: { }
promote:
type: boolean_checkbox
weight: 15
region: content
settings:
display_label: true
third_party_settings: { }
status:
type: boolean_checkbox
weight: 120
region: content
settings:
display_label: true
third_party_settings: { }
sticky:
type: boolean_checkbox
weight: 16
region: content
settings:
display_label: true
third_party_settings: { }
title:
type: string_textfield
weight: -5
region: content
settings:
size: 60
placeholder: ''
third_party_settings: { }
uid:
type: entity_reference_autocomplete type: entity_reference_autocomplete
weight: 5 weight: 2
region: content region: content
settings: settings:
match_operator: CONTAINS match_operator: CONTAINS
@@ -102,4 +79,37 @@ content:
size: 60 size: 60
placeholder: '' placeholder: ''
third_party_settings: { } third_party_settings: { }
hidden: { } langcode:
type: language_select
weight: 7
region: content
settings:
include_locked: true
third_party_settings: { }
path:
type: path
weight: 6
region: content
settings: { }
third_party_settings: { }
status:
type: boolean_checkbox
weight: 5
region: content
settings:
display_label: true
third_party_settings: { }
title:
type: string_textfield
weight: 0
region: content
settings:
size: 60
placeholder: ''
third_party_settings: { }
hidden:
created: true
promote: true
sticky: true
uid: true
url_redirects: true
@@ -4,12 +4,38 @@ status: true
dependencies: dependencies:
config: config:
- field.field.node.personne.body - field.field.node.personne.body
- field.field.node.personne.field_nom
- field.field.node.personne.field_prenom - field.field.node.personne.field_prenom
- field.field.node.personne.field_projet_s_lie_s
- field.field.node.personne.field_projets_curates
- field.field.node.personne.field_type_de_personne - field.field.node.personne.field_type_de_personne
- node.type.personne - node.type.personne
module: module:
- field_group
- path - path
- text - text
third_party_settings:
field_group:
group_details:
children:
- field_type_de_personne
- status
- path
- langcode
label: Details
region: content
parent_name: ''
weight: 5
format_type: details_sidebar
format_settings:
classes: ''
show_empty_fields: false
id: ''
label_as_html: false
open: true
description: ''
required_fields: true
weight: -100
id: node.personne.default id: node.personne.default
targetEntityType: node targetEntityType: node
bundle: personne bundle: personne
@@ -17,7 +43,7 @@ mode: default
content: content:
body: body:
type: text_textarea_with_summary type: text_textarea_with_summary
weight: 121 weight: 3
region: content region: content
settings: settings:
rows: 9 rows: 9
@@ -25,23 +51,45 @@ content:
placeholder: '' placeholder: ''
show_summary: false show_summary: false
third_party_settings: { } third_party_settings: { }
created: field_nom:
type: datetime_timestamp
weight: 10
region: content
settings: { }
third_party_settings: { }
field_prenom:
type: string_textfield type: string_textfield
weight: 122 weight: 2
region: content region: content
settings: settings:
size: 60 size: 60
placeholder: '' placeholder: ''
third_party_settings: { } third_party_settings: { }
field_prenom:
type: string_textfield
weight: 1
region: content
settings:
size: 60
placeholder: ''
third_party_settings: { }
field_projet_s_lie_s:
type: entity_reference_autocomplete
weight: 4
region: content
settings:
match_operator: CONTAINS
match_limit: 10
size: 60
placeholder: ''
third_party_settings: { }
field_projets_curates:
type: entity_reference_autocomplete
weight: 26
region: content
settings:
match_operator: CONTAINS
match_limit: 10
size: 60
placeholder: ''
third_party_settings: { }
field_type_de_personne: field_type_de_personne:
type: entity_reference_autocomplete type: entity_reference_autocomplete
weight: 123 weight: 11
region: content region: content
settings: settings:
match_operator: CONTAINS match_operator: CONTAINS
@@ -51,54 +99,35 @@ content:
third_party_settings: { } third_party_settings: { }
langcode: langcode:
type: language_select type: language_select
weight: 2 weight: 14
region: content region: content
settings: settings:
include_locked: true include_locked: true
third_party_settings: { } third_party_settings: { }
path: path:
type: path type: path
weight: 30 weight: 13
region: content region: content
settings: { } settings: { }
third_party_settings: { } third_party_settings: { }
promote:
type: boolean_checkbox
weight: 15
region: content
settings:
display_label: true
third_party_settings: { }
status: status:
type: boolean_checkbox type: boolean_checkbox
weight: 120 weight: 12
region: content
settings:
display_label: true
third_party_settings: { }
sticky:
type: boolean_checkbox
weight: 16
region: content region: content
settings: settings:
display_label: true display_label: true
third_party_settings: { } third_party_settings: { }
title: title:
type: string_textfield type: string_textfield
weight: -5 weight: 0
region: content region: content
settings: settings:
size: 60 size: 60
placeholder: '' placeholder: ''
third_party_settings: { } third_party_settings: { }
uid: hidden:
type: entity_reference_autocomplete created: true
weight: 5 promote: true
region: content sticky: true
settings: uid: true
match_operator: CONTAINS url_redirects: true
match_limit: 10
size: 60
placeholder: ''
third_party_settings: { }
hidden: { }
@@ -6,6 +6,7 @@ dependencies:
- field.field.node.projet.body - field.field.node.projet.body
- field.field.node.projet.field_artiste_s_invite_s - field.field.node.projet.field_artiste_s_invite_s
- field.field.node.projet.field_dates - field.field.node.projet.field_dates
- field.field.node.projet.field_fichiers
- field.field.node.projet.field_images - field.field.node.projet.field_images
- field.field.node.projet.field_invite_accompagne_curate_p - field.field.node.projet.field_invite_accompagne_curate_p
- field.field.node.projet.field_partenaire_s - field.field.node.projet.field_partenaire_s
@@ -18,6 +19,7 @@ dependencies:
- autocomplete_deluxe - autocomplete_deluxe
- datetime_range - datetime_range
- field_group - field_group
- file
- image - image
- path - path
- text - text
@@ -101,6 +103,7 @@ third_party_settings:
group_media: group_media:
children: children:
- field_images - field_images
- field_fichiers
label: Media label: Media
region: content region: content
parent_name: group_tabs parent_name: group_tabs
@@ -161,6 +164,13 @@ content:
region: content region: content
settings: { } settings: { }
third_party_settings: { } third_party_settings: { }
field_fichiers:
type: file_generic
weight: 8
region: content
settings:
progress_indicator: throbber
third_party_settings: { }
field_images: field_images:
type: image_image type: image_image
weight: 7 weight: 7
@@ -257,6 +267,11 @@ content:
size: 60 size: 60
placeholder: '' placeholder: ''
third_party_settings: { } third_party_settings: { }
url_redirects:
weight: 50
region: content
settings: { }
third_party_settings: { }
hidden: hidden:
created: true created: true
sticky: true sticky: true
@@ -6,6 +6,7 @@ dependencies:
- field.field.node.partenaire.body - field.field.node.partenaire.body
- field.field.node.partenaire.field_lien - field.field.node.partenaire.field_lien
- field.field.node.partenaire.field_logo - field.field.node.partenaire.field_logo
- field.field.node.partenaire.field_projet_s_lie_s
- node.type.partenaire - node.type.partenaire
module: module:
- image - image
@@ -47,6 +48,14 @@ content:
third_party_settings: { } third_party_settings: { }
weight: 103 weight: 103
region: content region: content
field_projet_s_lie_s:
type: entity_reference_label
label: above
settings:
link: true
third_party_settings: { }
weight: 108
region: content
links: links:
settings: { } settings: { }
third_party_settings: { } third_party_settings: { }
@@ -7,6 +7,7 @@ dependencies:
- field.field.node.partenaire.body - field.field.node.partenaire.body
- field.field.node.partenaire.field_lien - field.field.node.partenaire.field_lien
- field.field.node.partenaire.field_logo - field.field.node.partenaire.field_logo
- field.field.node.partenaire.field_projet_s_lie_s
- node.type.partenaire - node.type.partenaire
module: module:
- text - text
@@ -32,5 +33,6 @@ content:
hidden: hidden:
field_lien: true field_lien: true
field_logo: true field_logo: true
field_projet_s_lie_s: true
langcode: true langcode: true
search_api_excerpt: true search_api_excerpt: true
@@ -4,7 +4,10 @@ status: true
dependencies: dependencies:
config: config:
- field.field.node.personne.body - field.field.node.personne.body
- field.field.node.personne.field_nom
- field.field.node.personne.field_prenom - field.field.node.personne.field_prenom
- field.field.node.personne.field_projet_s_lie_s
- field.field.node.personne.field_projets_curates
- field.field.node.personne.field_type_de_personne - field.field.node.personne.field_type_de_personne
- node.type.personne - node.type.personne
module: module:
@@ -22,6 +25,14 @@ content:
third_party_settings: { } third_party_settings: { }
weight: 101 weight: 101
region: content region: content
field_nom:
type: string
label: above
settings:
link_to_entity: false
third_party_settings: { }
weight: 109
region: content
field_prenom: field_prenom:
type: string type: string
label: above label: above
@@ -30,6 +41,22 @@ content:
third_party_settings: { } third_party_settings: { }
weight: 102 weight: 102
region: content region: content
field_projet_s_lie_s:
type: entity_reference_label
label: above
settings:
link: true
third_party_settings: { }
weight: 108
region: content
field_projets_curates:
type: entity_reference_label
label: above
settings:
link: true
third_party_settings: { }
weight: 110
region: content
field_type_de_personne: field_type_de_personne:
type: entity_reference_label type: entity_reference_label
label: above label: above
@@ -5,32 +5,26 @@ dependencies:
config: config:
- core.entity_view_mode.node.teaser - core.entity_view_mode.node.teaser
- field.field.node.personne.body - field.field.node.personne.body
- field.field.node.personne.field_nom
- field.field.node.personne.field_prenom - field.field.node.personne.field_prenom
- field.field.node.personne.field_projet_s_lie_s
- field.field.node.personne.field_projets_curates
- field.field.node.personne.field_type_de_personne - field.field.node.personne.field_type_de_personne
- node.type.personne - node.type.personne
module: module:
- text
- user - user
id: node.personne.teaser id: node.personne.teaser
targetEntityType: node targetEntityType: node
bundle: personne bundle: personne
mode: teaser mode: teaser
content: content: { }
body:
type: text_summary_or_trimmed
label: hidden
settings:
trim_length: 600
third_party_settings: { }
weight: 101
region: content
links:
settings: { }
third_party_settings: { }
weight: 100
region: content
hidden: hidden:
body: true
field_nom: true
field_prenom: true field_prenom: true
field_projet_s_lie_s: true
field_projets_curates: true
field_type_de_personne: true field_type_de_personne: true
langcode: true langcode: true
links: true
search_api_excerpt: true search_api_excerpt: true
@@ -6,6 +6,7 @@ dependencies:
- field.field.node.projet.body - field.field.node.projet.body
- field.field.node.projet.field_artiste_s_invite_s - field.field.node.projet.field_artiste_s_invite_s
- field.field.node.projet.field_dates - field.field.node.projet.field_dates
- field.field.node.projet.field_fichiers
- field.field.node.projet.field_images - field.field.node.projet.field_images
- field.field.node.projet.field_invite_accompagne_curate_p - field.field.node.projet.field_invite_accompagne_curate_p
- field.field.node.projet.field_partenaire_s - field.field.node.projet.field_partenaire_s
@@ -15,6 +16,7 @@ dependencies:
- node.type.projet - node.type.projet
module: module:
- datetime_range - datetime_range
- file
- image - image
- options - options
- text - text
@@ -29,7 +31,7 @@ content:
label: hidden label: hidden
settings: { } settings: { }
third_party_settings: { } third_party_settings: { }
weight: 101 weight: 1
region: content region: content
field_artiste_s_invite_s: field_artiste_s_invite_s:
type: entity_reference_label type: entity_reference_label
@@ -37,18 +39,26 @@ content:
settings: settings:
link: true link: true
third_party_settings: { } third_party_settings: { }
weight: 105 weight: 6
region: content region: content
field_dates: field_dates:
type: daterange_default type: daterange_default
label: above label: visually_hidden
settings: settings:
timezone_override: '' timezone_override: ''
format_type: medium format_type: medium
from_to: both from_to: both
separator: '-' separator: '-'
third_party_settings: { } third_party_settings: { }
weight: 102 weight: 2
region: content
field_fichiers:
type: file_default
label: above
settings:
use_description_as_link_text: true
third_party_settings: { }
weight: 9
region: content region: content
field_images: field_images:
type: image type: image
@@ -59,7 +69,7 @@ content:
image_loading: image_loading:
attribute: lazy attribute: lazy
third_party_settings: { } third_party_settings: { }
weight: 109 weight: 10
region: content region: content
field_invite_accompagne_curate_p: field_invite_accompagne_curate_p:
type: entity_reference_label type: entity_reference_label
@@ -67,7 +77,7 @@ content:
settings: settings:
link: true link: true
third_party_settings: { } third_party_settings: { }
weight: 106 weight: 4
region: content region: content
field_partenaire_s: field_partenaire_s:
type: entity_reference_label type: entity_reference_label
@@ -75,14 +85,14 @@ content:
settings: settings:
link: true link: true
third_party_settings: { } third_party_settings: { }
weight: 104 weight: 5
region: content region: content
field_portage: field_portage:
type: list_default type: list_default
label: above label: above
settings: { } settings: { }
third_party_settings: { } third_party_settings: { }
weight: 107 weight: 7
region: content region: content
field_projet_s_lie_s: field_projet_s_lie_s:
type: entity_reference_label type: entity_reference_label
@@ -90,20 +100,20 @@ content:
settings: settings:
link: true link: true
third_party_settings: { } third_party_settings: { }
weight: 108 weight: 8
region: content region: content
field_type_de_projet: field_type_de_projet:
type: entity_reference_label type: entity_reference_label
label: above label: visually_hidden
settings: settings:
link: true link: true
third_party_settings: { } third_party_settings: { }
weight: 103 weight: 3
region: content region: content
links: links:
settings: { } settings: { }
third_party_settings: { } third_party_settings: { }
weight: 100 weight: 0
region: content region: content
hidden: hidden:
langcode: true langcode: true
@@ -0,0 +1,116 @@
uuid: 3e27c55e-5165-4118-bd03-5c0433642e42
langcode: fr
status: true
dependencies:
config:
- core.entity_view_mode.node.full
- field.field.node.projet.body
- field.field.node.projet.field_artiste_s_invite_s
- field.field.node.projet.field_dates
- field.field.node.projet.field_fichiers
- field.field.node.projet.field_images
- field.field.node.projet.field_invite_accompagne_curate_p
- field.field.node.projet.field_partenaire_s
- field.field.node.projet.field_portage
- field.field.node.projet.field_projet_s_lie_s
- field.field.node.projet.field_type_de_projet
- image.style.large
- node.type.projet
module:
- datetime_range
- file
- image_field_caption
- layout_builder
- options
- text
- user
third_party_settings:
layout_builder:
enabled: false
allow_custom: false
id: node.projet.full
targetEntityType: node
bundle: projet
mode: full
content:
body:
type: text_default
label: hidden
settings: { }
third_party_settings: { }
weight: 3
region: content
field_artiste_s_invite_s:
type: entity_reference_label
label: hidden
settings:
link: true
third_party_settings: { }
weight: 0
region: content
field_dates:
type: daterange_default
label: hidden
settings:
timezone_override: ''
format_type: d_m_y
from_to: both
separator: '-'
third_party_settings: { }
weight: 2
region: content
field_fichiers:
type: file_default
label: above
settings:
use_description_as_link_text: true
third_party_settings: { }
weight: 7
region: content
field_images:
type: image_caption
label: hidden
settings:
image_style: large
image_link: file
image_loading:
attribute: lazy
third_party_settings: { }
weight: 8
region: content
field_invite_accompagne_curate_p:
type: entity_reference_label
label: above
settings:
link: true
third_party_settings: { }
weight: 1
region: content
field_partenaire_s:
type: entity_reference_label
label: above
settings:
link: true
third_party_settings: { }
weight: 4
region: content
field_portage:
type: list_default
label: above
settings: { }
third_party_settings: { }
weight: 5
region: content
field_projet_s_lie_s:
type: entity_reference_label
label: above
settings:
link: true
third_party_settings: { }
weight: 6
region: content
hidden:
field_type_de_projet: true
langcode: true
links: true
search_api_excerpt: true
@@ -7,14 +7,17 @@ dependencies:
- field.field.node.projet.body - field.field.node.projet.body
- field.field.node.projet.field_artiste_s_invite_s - field.field.node.projet.field_artiste_s_invite_s
- field.field.node.projet.field_dates - field.field.node.projet.field_dates
- field.field.node.projet.field_fichiers
- field.field.node.projet.field_images - field.field.node.projet.field_images
- field.field.node.projet.field_invite_accompagne_curate_p - field.field.node.projet.field_invite_accompagne_curate_p
- field.field.node.projet.field_partenaire_s - field.field.node.projet.field_partenaire_s
- field.field.node.projet.field_portage - field.field.node.projet.field_portage
- field.field.node.projet.field_projet_s_lie_s - field.field.node.projet.field_projet_s_lie_s
- field.field.node.projet.field_type_de_projet - field.field.node.projet.field_type_de_projet
- image.style.medium
- node.type.projet - node.type.projet
module: module:
- image_delta_formatter
- text - text
- user - user
id: node.projet.teaser id: node.projet.teaser
@@ -26,23 +29,40 @@ content:
type: text_summary_or_trimmed type: text_summary_or_trimmed
label: hidden label: hidden
settings: settings:
trim_length: 600 trim_length: 100
third_party_settings: { } third_party_settings: { }
weight: 101 weight: 2
region: content region: content
links: field_artiste_s_invite_s:
settings: { } type: entity_reference_label
label: hidden
settings:
link: false
third_party_settings: { } third_party_settings: { }
weight: 100 weight: 1
region: content
field_images:
type: image_delta_formatter
label: hidden
settings:
deltas:
- 1
deltas_reversed: false
image_link: content
image_style: medium
image_loading:
attribute: lazy
third_party_settings: { }
weight: 0
region: content region: content
hidden: hidden:
field_artiste_s_invite_s: true
field_dates: true field_dates: true
field_images: true field_fichiers: true
field_invite_accompagne_curate_p: true field_invite_accompagne_curate_p: true
field_partenaire_s: true field_partenaire_s: true
field_portage: true field_portage: true
field_projet_s_lie_s: true field_projet_s_lie_s: true
field_type_de_projet: true field_type_de_projet: true
langcode: true langcode: true
links: true
search_api_excerpt: true search_api_excerpt: true
+17
View File
@@ -1,6 +1,7 @@
_core: _core:
default_config_hash: 4GIX5Esnc_umpXUBj4IIocRX7Mt5fPhm4AgXfE3E56E default_config_hash: 4GIX5Esnc_umpXUBj4IIocRX7Mt5fPhm4AgXfE3E56E
module: module:
auto_entitylabel: -100
actions_permissions: 0 actions_permissions: 0
address: 0 address: 0
admin_toolbar: 0 admin_toolbar: 0
@@ -52,8 +53,12 @@ module:
help: 0 help: 0
honeypot: 0 honeypot: 0
image: 0 image: 0
image_delta_formatter: 0
image_field_caption: 0
inline_entity_form: 0 inline_entity_form: 0
language: 0 language: 0
layout_builder: 0
layout_discovery: 0
link: 0 link: 0
link_attributes: 0 link_attributes: 0
linkit: 0 linkit: 0
@@ -63,22 +68,32 @@ module:
menu_block: 0 menu_block: 0
menu_link_content: 0 menu_link_content: 0
menu_ui: 0 menu_ui: 0
migrate: 0
migrate_google_sheets: 0
migrate_leshed: 0
migrate_plus: 0
migrate_tools: 0
mysql: 0 mysql: 0
node: 0 node: 0
options: 0 options: 0
page_cache: 0 page_cache: 0
page_manager: 0
page_manager_ui: 0
pagerer: 0 pagerer: 0
path: 0 path: 0
path_alias: 0 path_alias: 0
path_alias_xt: 0 path_alias_xt: 0
pathologic: 0 pathologic: 0
persistent_login: 0 persistent_login: 0
redirect: 0
redis: 0 redis: 0
responsive_image: 0 responsive_image: 0
search_api: 0 search_api: 0
search_api_db: 0 search_api_db: 0
shield: 0
smart_trim: 0 smart_trim: 0
smtp: 0 smtp: 0
structure_sync: 0
system: 0 system: 0
taxonomy: 0 taxonomy: 0
telephone: 0 telephone: 0
@@ -101,4 +116,6 @@ module:
theme: theme:
seven: 0 seven: 0
adminimal_theme: 0 adminimal_theme: 0
stark: 0
leshed: 0
profile: figli_starter_kit profile: figli_starter_kit
@@ -0,0 +1,29 @@
uuid: 47db4d6d-cf31-4049-b817-22c99a51e797
langcode: fr
status: true
dependencies:
config:
- field.storage.node.field_projet_s_lie_s
- node.type.partenaire
- node.type.projet
id: node.partenaire.field_projet_s_lie_s
field_name: field_projet_s_lie_s
entity_type: node
bundle: partenaire
label: 'Projet(s) lié(s)'
description: ''
required: false
translatable: false
default_value: { }
default_value_callback: ''
settings:
handler: 'default:node'
handler_settings:
target_bundles:
projet: projet
sort:
field: _none
direction: ASC
auto_create: false
auto_create_bundle: projet
field_type: entity_reference
@@ -0,0 +1,19 @@
uuid: 7f1a16d3-57ea-4861-ad4f-1312ceadd1e7
langcode: fr
status: true
dependencies:
config:
- field.storage.node.field_nom
- node.type.personne
id: node.personne.field_nom
field_name: field_nom
entity_type: node
bundle: personne
label: Nom
description: ''
required: true
translatable: false
default_value: { }
default_value_callback: ''
settings: { }
field_type: string
@@ -11,7 +11,7 @@ entity_type: node
bundle: personne bundle: personne
label: Prenom label: Prenom
description: '' description: ''
required: false required: true
translatable: false translatable: false
default_value: { } default_value: { }
default_value_callback: '' default_value_callback: ''
@@ -0,0 +1,29 @@
uuid: d3189e52-d468-4bee-8463-5067aeb810c7
langcode: fr
status: true
dependencies:
config:
- field.storage.node.field_projet_s_lie_s
- node.type.personne
- node.type.projet
id: node.personne.field_projet_s_lie_s
field_name: field_projet_s_lie_s
entity_type: node
bundle: personne
label: 'Projet(s) lié(s)'
description: ''
required: false
translatable: false
default_value: { }
default_value_callback: ''
settings:
handler: 'default:node'
handler_settings:
target_bundles:
projet: projet
sort:
field: _none
direction: ASC
auto_create: false
auto_create_bundle: projet
field_type: entity_reference
@@ -0,0 +1,29 @@
uuid: 75b15206-7946-48cb-b7b9-17c27ad00c25
langcode: fr
status: true
dependencies:
config:
- field.storage.node.field_projets_curates
- node.type.personne
- node.type.projet
id: node.personne.field_projets_curates
field_name: field_projets_curates
entity_type: node
bundle: personne
label: projets_curates
description: ''
required: false
translatable: false
default_value: { }
default_value_callback: ''
settings:
handler: 'default:node'
handler_settings:
target_bundles:
projet: projet
sort:
field: _none
direction: ASC
auto_create: false
auto_create_bundle: ''
field_type: entity_reference
@@ -0,0 +1,27 @@
uuid: 01cf5142-1617-4b0a-afbe-46bed19f3a75
langcode: fr
status: true
dependencies:
config:
- field.storage.node.field_fichiers
- node.type.projet
module:
- file
id: node.projet.field_fichiers
field_name: field_fichiers
entity_type: node
bundle: projet
label: Fichiers
description: ''
required: false
translatable: false
default_value: { }
default_value_callback: ''
settings:
handler: 'default:file'
handler_settings: { }
file_directory: '[date:custom:Y]-[date:custom:m]'
file_extensions: 'pdf doc docx odt zip txt'
max_filesize: ''
description_field: true
field_type: file
@@ -35,4 +35,7 @@ settings:
title: '' title: ''
width: null width: null
height: null height: null
caption: ''
caption_field: 1
caption_field_required: 0
field_type: image field_type: image
@@ -0,0 +1,23 @@
uuid: 9a9ebb54-2e2f-46bd-9586-60ebedfc95d8
langcode: fr
status: true
dependencies:
module:
- file
- node
id: node.field_fichiers
field_name: field_fichiers
entity_type: node
type: file
settings:
target_type: file
display_field: true
display_default: false
uri_scheme: public
module: file
locked: false
cardinality: -1
translatable: true
indexes: { }
persist_with_no_fields: false
custom_storage: false
@@ -0,0 +1,21 @@
uuid: 14aa5aef-0840-4e3d-a78a-027e2c61844f
langcode: fr
status: true
dependencies:
module:
- node
id: node.field_nom
field_name: field_nom
entity_type: node
type: string
settings:
max_length: 255
case_sensitive: false
is_ascii: false
module: core
locked: false
cardinality: 1
translatable: true
indexes: { }
persist_with_no_fields: false
custom_storage: false
@@ -0,0 +1,19 @@
uuid: c5d97546-10d2-46e0-9324-1164bdd04cbb
langcode: fr
status: true
dependencies:
module:
- node
id: node.field_projets_curates
field_name: field_projets_curates
entity_type: node
type: entity_reference
settings:
target_type: node
module: core
locked: false
cardinality: -1
translatable: true
indexes: { }
persist_with_no_fields: false
custom_storage: false
@@ -0,0 +1,13 @@
uuid: bc346faf-2574-409d-bbe1-325f4498ade5
langcode: en
status: true
dependencies:
module:
- redirect
_core:
default_config_hash: y734AXEpjry1Ppff_hQQBFQGJvYjF0fk1JoWtp5q2JY
id: redirect.redirect
target_entity_type_id: redirect
target_bundle: redirect
default_langcode: und
language_alterable: true
@@ -0,0 +1,153 @@
uuid: a3812d6e-7a67-4110-bb47-d1062709f806
langcode: fr
status: true
dependencies:
- migrate_google_sheets
- migrate_plus
id: projet_node
class: null
field_plugin_method: null
cck_plugin_method: null
migration_tags: null
migration_group: le_shed
label: 'Import des projets depuis Google Sheets'
source:
plugin: projet_node
data_fetcher_plugin: http
data_parser_plugin: google_sheets
constants:
images_destination: 'public://projets/'
urls: 'https://sheets.googleapis.com/v4/spreadsheets/1561Gv1C-IlCTmg64Z8HnznvOeLbvwhA_k1l6aq95zFA/values/Projets'
fields:
-
name: id
label: 'Unique identifier'
selector: UID
-
name: title
label: Title
selector: 'Nom du projet'
-
name: date_start
label: 'Date start'
selector: 'Date de début de projet'
-
name: date_end
label: 'Date end'
selector: 'Date de fin de projet'
-
name: projet_type
label: 'Type de projet'
selector: 'Type de projet'
-
name: curator
label: Commissaria
selector: 'invité/accompagné/curaté par'
-
name: artistes
label: 'Artistes invité·es'
selector: 'Artistes invité·es'
-
name: partners
label: 'Partenaires associés'
selector: 'Partenaires associés'
ids:
id:
type: integer
process:
type:
plugin: default_value
default_value: projet
title:
plugin: skip_on_empty
method: row
source: title
message: 'Projet ignoré : « Nom du projet » vide.'
body/value: body_html
body/format:
plugin: default_value
default_value: wysiwyg
field_images:
plugin: sub_process
source: images
process:
target_id:
plugin: image_resize_import
source: uri
destination: destination
max_width: 1920
max_height: 1920
reuse: true
skip_on_missing_source: true
alt: alt
image_field_caption/value: caption
image_field_caption/format:
plugin: default_value
default_value: wysiwyg
field_fichiers:
plugin: sub_process
source: pdfs
process:
target_id:
plugin: file_import
source: uri
destination: destination
reuse: true
skip_on_missing_source: true
description: description
field_dates/value:
plugin: leshed_date
source: date_start
field_dates/end_value:
plugin: leshed_date
source: date_end
end_of_year: true
fallback_source: date_start
field_type_de_projet:
-
plugin: skip_on_empty
method: process
source: projet_type
-
plugin: explode
delimiter: ','
-
plugin: callback
callable: trim
-
plugin: entity_generate
entity_type: taxonomy_term
bundle_key: vid
bundle: type_de_projet
value_key: name
field_invite_accompagne_curate_p:
-
plugin: skip_on_empty
method: process
source: curator
-
plugin: personne_generate
type_de_personne: curateur
field_artiste_s_invite_s:
-
plugin: skip_on_empty
method: process
source: artistes
-
plugin: personne_generate
type_de_personne: artiste
field_partenaire_s:
-
plugin: skip_on_empty
method: process
source: partners
-
plugin: partenaire_generate
uid:
plugin: default_value
default_value: 3
destination:
plugin: 'entity:node'
idMap: { }
migration_dependencies: { }
include: null
@@ -0,0 +1,10 @@
uuid: 98ad5e45-0e03-4a77-97e3-93fe57fee1ed
langcode: fr
status: true
dependencies: { }
id: le_shed
label: 'Le shed'
description: ''
source_type: 'google doc sheets'
module: null
shared_configuration: null
+12
View File
@@ -0,0 +1,12 @@
uuid: f3e3d3b8-ba2d-4c20-b368-bbbfe1b8f5ce
langcode: fr
status: true
dependencies: { }
id: home
label: Home
description: ''
use_admin_theme: false
path: /home
access_logic: and
access_conditions: { }
parameters: { }
@@ -0,0 +1,18 @@
uuid: 4882a420-a3d2-4c11-b38d-0d7bd7372336
langcode: fr
status: true
dependencies: { }
_core:
default_config_hash: RCVWP-yHwxSNiQORMIabDgHMEVqOMW58w80BQgRFJ4k
id: node_view
label: 'Vue du nœud'
description: 'When enabled, this overrides the default Drupal behavior for displaying nodes at <em>/node/{node}</em>. If you add variants, you may use selection criteria such as node type or language or user access to provide different views of nodes. If no variant is selected, the default Drupal node view will be used. This page only affects nodes viewed as pages, it will not affect nodes viewed in lists or at other locations.'
use_admin_theme: false
path: '/node/{node}'
access_logic: and
access_conditions: { }
parameters:
node:
machine_name: node
label: Nœud
type: 'entity:node'
@@ -0,0 +1,60 @@
uuid: 2c2a0935-3f55-4a6e-a4ac-592e546657f8
langcode: fr
status: true
dependencies:
config:
- page_manager.page.home
- views.view.home
module:
- layout_builder
- views
id: home-layout_builder-0
label: 'Layout Builder'
weight: 0
variant: layout_builder
variant_settings:
id: layout_builder
label: null
weight: 0
uuid: 5e526712-83fd-4aeb-83b6-974ad13c73bd
sections:
-
layout_id: layout_twocol_section
layout_settings:
label: ''
context_mapping: { }
column_widths: 50-50
components:
befea5c2-ca15-453e-b3fa-3a5cd4964d39:
uuid: befea5c2-ca15-453e-b3fa-3a5cd4964d39
region: first
configuration:
id: 'views_block:home-block_1'
label: ''
label_display: '0'
provider: views
context_mapping: { }
views_label: ''
items_per_page: none
exposed: { }
weight: 0
additional: { }
f872a8d1-ade5-4443-8399-d58fabd16bf3:
uuid: f872a8d1-ade5-4443-8399-d58fabd16bf3
region: second
configuration:
id: 'views_block:home-block_2'
label: ''
label_display: '0'
provider: views
context_mapping: { }
views_label: ''
items_per_page: none
exposed: { }
weight: 0
additional: { }
third_party_settings: { }
page: home
selection_criteria: { }
selection_logic: and
static_context: { }
+1 -1
View File
@@ -1,3 +1,4 @@
langcode: fr
_core: _core:
default_config_hash: R7HmKRbuDsDQNpqEWt7wWcf0ygaE3u-NQQr-w_LhHJk default_config_hash: R7HmKRbuDsDQNpqEWt7wWcf0ygaE3u-NQQr-w_LhHJk
auto_redirect: true auto_redirect: true
@@ -7,4 +8,3 @@ warning: false
ignore_admin_path: false ignore_admin_path: false
access_check: false access_check: false
route_normalizer_enabled: true route_normalizer_enabled: true
langcode: fr
+17
View File
@@ -0,0 +1,17 @@
_core:
default_config_hash: FLJZME3II5fjmMSrdEvJHp95ZJf8V_X0jbX0VdVU1BU
shield_enable: true
credential_provider: shield
credentials:
shield:
user: leshed
pass: ztE5cpdhHdfY5w
allow_cli: true
debug_header: false
unset_basic_auth_headers: true
print: Hello!
method: 0
paths: ''
allowlist: ''
http_method_allowlist: { }
domains: ''
+14
View File
@@ -0,0 +1,14 @@
menus:
-
menu_name: main
title: Accueil
parent: null
uri: 'internal:/'
link_title: ''
description: null
enabled: true
expanded: false
weight: 0
langcode: fr
uuid: 75910d9e-f8a0-4dbd-930f-e37fb47de597
options: { }
@@ -0,0 +1,13 @@
uuid: 526a6b2c-b190-400b-871c-f05493f0d560
langcode: fr
status: true
dependencies:
module:
- redirect
_core:
default_config_hash: 2T-yEyZvw_9I2XVc5fquqcWBvzm6y2o3be2YbQwKe8E
id: redirect_delete_action
label: 'Supprimer une redirection'
type: redirect
plugin: redirect_delete_action
configuration: { }
+2 -2
View File
@@ -4,11 +4,11 @@ langcode: fr
uuid: e53d1b41-902a-4030-a476-ad2d5f3e5070 uuid: e53d1b41-902a-4030-a476-ad2d5f3e5070
name: 'Le Shed' name: 'Le Shed'
mail: dev@figureslibres.io mail: dev@figureslibres.io
slogan: '' slogan: "Centre d'art en Normandie"
page: page:
403: '' 403: ''
404: '' 404: ''
front: /user/login front: /home
admin_compact_mode: false admin_compact_mode: false
weight_select_max: 100 weight_select_max: 100
default_langcode: fr default_langcode: fr
+2 -1
View File
@@ -1,4 +1,5 @@
_core: _core:
default_config_hash: TpdA4rsOBAawnqeon9A-Ooht_4VO3hHKib79xyHcTyg default_config_hash: TpdA4rsOBAawnqeon9A-Ooht_4VO3hHKib79xyHcTyg
admin: adminimal_theme
langcode: fr langcode: fr
admin: adminimal_theme
default: leshed
+292 -72
View File
@@ -2,6 +2,10 @@ uuid: 34d30e20-1672-4c74-b35d-5f4312f520f2
langcode: fr langcode: fr
status: true status: true
dependencies: dependencies:
config:
- field.storage.node.field_projet_s_lie_s
- field.storage.node.field_projets_curates
- field.storage.node.field_type_de_projet
module: module:
- node - node
- user - user
@@ -39,26 +43,6 @@ display:
hide_empty: false hide_empty: false
empty_zero: false empty_zero: false
hide_alter_empty: true hide_alter_empty: true
title:
id: title
table: node_field_data
field: title
entity_type: node
entity_field: title
plugin_id: field
label: Titre
exclude: false
alter:
alter_text: false
element_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
type: string
settings:
link_to_entity: true
type: type:
id: type id: type
table: node_field_data table: node_field_data
@@ -124,25 +108,260 @@ display:
multi_type: separator multi_type: separator
separator: ', ' separator: ', '
field_api_classes: false field_api_classes: false
name: title:
id: name id: title
table: users_field_data table: node_field_data
field: name field: title
relationship: uid relationship: none
entity_type: user group_type: group
entity_field: name admin_label: ''
entity_type: node
entity_field: title
plugin_id: field plugin_id: field
label: Auteur label: Titre
exclude: false exclude: false
alter: alter:
alter_text: false alter_text: false
text: '{{ field_prenom }} {{ title }}'
make_link: false
path: ''
absolute: false
external: false
replace_spaces: false
path_case: none
trim_whitespace: false
alt: ''
rel: ''
link_class: ''
prefix: ''
suffix: ''
target: ''
nl2br: false
max_length: 0
word_boundary: true
ellipsis: true
more_link: false
more_link_text: ''
more_link_path: ''
strip_tags: false
trim: false
preserve_tags: ''
html: false
element_type: ''
element_class: '' element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: true
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true element_default_classes: true
empty: '' empty: ''
hide_empty: false hide_empty: false
empty_zero: false empty_zero: false
hide_alter_empty: true hide_alter_empty: true
type: user_name click_sort_column: value
type: string
settings:
link_to_entity: true
group_column: value
group_columns: { }
group_rows: true
delta_limit: 0
delta_offset: 0
delta_reversed: false
delta_first_last: false
multi_type: separator
separator: ', '
field_api_classes: false
field_type_de_projet:
id: field_type_de_projet
table: node__field_type_de_projet
field: field_type_de_projet
relationship: none
group_type: group
admin_label: ''
plugin_id: field
label: 'Type de projet'
exclude: false
alter:
alter_text: false
text: ''
make_link: false
path: ''
absolute: false
external: false
replace_spaces: false
path_case: none
trim_whitespace: false
alt: ''
rel: ''
link_class: ''
prefix: ''
suffix: ''
target: ''
nl2br: false
max_length: 0
word_boundary: true
ellipsis: true
more_link: false
more_link_text: ''
more_link_path: ''
strip_tags: false
trim: false
preserve_tags: ''
html: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: true
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
click_sort_column: target_id
type: entity_reference_label
settings:
link: false
group_column: target_id
group_columns: { }
group_rows: true
delta_limit: 0
delta_offset: 0
delta_reversed: false
delta_first_last: false
multi_type: separator
separator: ', '
field_api_classes: false
field_projet_s_lie_s:
id: field_projet_s_lie_s
table: node__field_projet_s_lie_s
field: field_projet_s_lie_s
relationship: none
group_type: group
admin_label: ''
plugin_id: field
label: 'Projet(s) lié(s)'
exclude: false
alter:
alter_text: false
text: ''
make_link: false
path: ''
absolute: false
external: false
replace_spaces: false
path_case: none
trim_whitespace: false
alt: ''
rel: ''
link_class: ''
prefix: ''
suffix: ''
target: ''
nl2br: false
max_length: 0
word_boundary: true
ellipsis: true
more_link: false
more_link_text: ''
more_link_path: ''
strip_tags: false
trim: false
preserve_tags: ''
html: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: true
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
click_sort_column: target_id
type: entity_reference_label
settings:
link: true
group_column: target_id
group_columns: { }
group_rows: true
delta_limit: 0
delta_offset: 0
delta_reversed: false
delta_first_last: false
multi_type: separator
separator: ', '
field_api_classes: false
field_projets_curates:
id: field_projets_curates
table: node__field_projets_curates
field: field_projets_curates
relationship: none
group_type: group
admin_label: ''
plugin_id: field
label: projets_curates
exclude: false
alter:
alter_text: false
text: ''
make_link: false
path: ''
absolute: false
external: false
replace_spaces: false
path_case: none
trim_whitespace: false
alt: ''
rel: ''
link_class: ''
prefix: ''
suffix: ''
target: ''
nl2br: false
max_length: 0
word_boundary: true
ellipsis: true
more_link: false
more_link_text: ''
more_link_path: ''
strip_tags: false
trim: false
preserve_tags: ''
html: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: true
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
click_sort_column: target_id
type: entity_reference_label
settings:
link: true
group_column: target_id
group_columns: { }
group_rows: true
delta_limit: 0
delta_offset: 0
delta_reversed: false
delta_first_last: false
multi_type: separator
separator: ', '
field_api_classes: false
status: status:
id: status id: status
table: node_field_data table: node_field_data
@@ -249,13 +468,13 @@ display:
pager: pager:
type: full type: full
options: options:
pagination_heading_level: h4
items_per_page: 50 items_per_page: 50
tags: tags:
next: 'Suivant ' next: 'Suivant '
previous: ' Précédent' previous: ' Précédent'
first: '« Premier' first: '« Premier'
last: 'Dernier »' last: 'Dernier »'
pagination_heading_level: h4
exposed_form: exposed_form:
type: basic type: basic
options: options:
@@ -483,15 +702,14 @@ display:
default_row_class: true default_row_class: true
columns: columns:
node_bulk_form: node_bulk_form node_bulk_form: node_bulk_form
type: title
title: title title: title
type: type field_type_de_projet: field_type_de_projet
name: name field_projets_curates: field_projet_s_lie_s
field_projet_s_lie_s: field_projet_s_lie_s
status: status status: status
changed: changed changed: changed
edit_node: edit_node operations: operations
delete_node: delete_node
dropbutton: dropbutton
timestamp: title
default: changed default: changed
info: info:
node_bulk_form: node_bulk_form:
@@ -499,13 +717,6 @@ display:
separator: '' separator: ''
empty_column: false empty_column: false
responsive: '' responsive: ''
title:
sortable: true
default_sort_order: asc
align: ''
separator: ''
empty_column: false
responsive: ''
type: type:
sortable: true sortable: true
default_sort_order: asc default_sort_order: asc
@@ -513,13 +724,28 @@ display:
separator: '' separator: ''
empty_column: false empty_column: false
responsive: '' responsive: ''
name: title:
sortable: false sortable: true
default_sort_order: asc default_sort_order: asc
align: ''
separator: '<br/>'
empty_column: false
responsive: ''
field_type_de_projet:
align: '' align: ''
separator: '' separator: ''
empty_column: false empty_column: false
responsive: priority-low responsive: ''
field_projets_curates:
align: ''
separator: ''
empty_column: false
responsive: ''
field_projet_s_lie_s:
align: ''
separator: ''
empty_column: false
responsive: ''
status: status:
sortable: true sortable: true
default_sort_order: asc default_sort_order: asc
@@ -534,30 +760,7 @@ display:
separator: '' separator: ''
empty_column: false empty_column: false
responsive: priority-low responsive: priority-low
edit_node: operations:
sortable: false
default_sort_order: asc
align: ''
separator: ''
empty_column: false
responsive: ''
delete_node:
sortable: false
default_sort_order: asc
align: ''
separator: ''
empty_column: false
responsive: ''
dropbutton:
sortable: false
default_sort_order: asc
align: ''
separator: ''
empty_column: false
responsive: ''
timestamp:
sortable: false
default_sort_order: asc
align: '' align: ''
separator: '' separator: ''
empty_column: false empty_column: false
@@ -581,6 +784,17 @@ display:
plugin_id: standard plugin_id: standard
required: true required: true
show_admin_links: false show_admin_links: false
header:
result:
id: result
table: views
field: result
relationship: none
group_type: group
admin_label: ''
plugin_id: result
empty: false
content: 'Affichage de @start à @end sur @total'
display_extenders: { } display_extenders: { }
cache_metadata: cache_metadata:
max-age: 0 max-age: 0
@@ -592,7 +806,10 @@ display:
- user - user
- 'user.node_grants:view' - 'user.node_grants:view'
- user.permissions - user.permissions
tags: { } tags:
- 'config:field.storage.node.field_projet_s_lie_s'
- 'config:field.storage.node.field_projets_curates'
- 'config:field.storage.node.field_type_de_projet'
page_1: page_1:
id: page_1 id: page_1
display_title: Page display_title: Page
@@ -624,4 +841,7 @@ display:
- user - user
- 'user.node_grants:view' - 'user.node_grants:view'
- user.permissions - user.permissions
tags: { } tags:
- 'config:field.storage.node.field_projet_s_lie_s'
- 'config:field.storage.node.field_projets_curates'
- 'config:field.storage.node.field_type_de_projet'
+461
View File
@@ -0,0 +1,461 @@
uuid: 6cad35b8-8332-4db2-a4a1-4cd81706925f
langcode: fr
status: true
dependencies:
config:
- core.entity_view_mode.node.teaser
- taxonomy.vocabulary.type_de_projet
content:
- 'taxonomy_term:type_de_projet:1c5e92c0-6d55-4cdd-83de-6914dc61346f'
- 'taxonomy_term:type_de_projet:69364af4-a6cf-408c-a03a-8efa83460a01'
- 'taxonomy_term:type_de_projet:6e187150-5f46-46ec-9b05-a6add0851f7a'
- 'taxonomy_term:type_de_projet:74ca42bd-d2d1-4e68-b684-5ccee09c3435'
- 'taxonomy_term:type_de_projet:8c83ec08-8c52-4636-8a9f-8d43ca6eaa96'
- 'taxonomy_term:type_de_projet:ab8d04d2-57ef-48e1-b1c9-17e7ff068534'
- 'taxonomy_term:type_de_projet:c8fe49b6-d54b-469b-b6a1-a28779c0759f'
- 'taxonomy_term:type_de_projet:dcfda362-9cff-4d7a-9a6d-bf8dd57c2de7'
- 'taxonomy_term:type_de_projet:f35aa501-6be1-478d-8a36-da8977836978'
module:
- datetime
- node
- taxonomy
- user
id: home
label: Home
module: views
description: ''
tag: ''
base_table: node_field_data
base_field: nid
display:
default:
id: default
display_title: Default
display_plugin: default
position: 0
display_options:
title: Home
fields:
title:
id: title
table: node_field_data
field: title
relationship: none
group_type: group
admin_label: ''
entity_type: node
entity_field: title
plugin_id: field
label: ''
exclude: false
alter:
alter_text: false
make_link: false
absolute: false
word_boundary: false
ellipsis: false
strip_tags: false
trim: false
html: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: true
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
click_sort_column: value
type: string
settings:
link_to_entity: true
group_column: value
group_columns: { }
group_rows: true
delta_limit: 0
delta_offset: 0
delta_reversed: false
delta_first_last: false
multi_type: separator
separator: ', '
field_api_classes: false
pager:
type: some
options:
offset: 0
items_per_page: 20
exposed_form:
type: basic
options:
submit_button: Appliquer
reset_button: false
reset_button_label: Réinitialiser
exposed_sorts_label: 'Trier par'
expose_sort_order: true
sort_asc_label: Asc
sort_desc_label: Desc
access:
type: perm
options:
perm: 'access content'
cache:
type: tag
options: { }
empty: { }
sorts:
field_dates_value:
id: field_dates_value
table: node__field_dates
field: field_dates_value
relationship: none
group_type: group
admin_label: ''
plugin_id: datetime
order: DESC
expose:
label: ''
field_identifier: ''
exposed: false
granularity: second
arguments: { }
filters:
status:
id: status
table: node_field_data
field: status
entity_type: node
entity_field: status
plugin_id: boolean
value: '1'
group: 1
expose:
operator: ''
field_type_de_projet_target_id:
id: field_type_de_projet_target_id
table: node__field_type_de_projet
field: field_type_de_projet_target_id
relationship: none
group_type: group
admin_label: ''
plugin_id: taxonomy_index_tid
operator: or
value:
11: 11
1: 1
2: 2
group: 1
exposed: false
expose:
operator_id: ''
label: ''
description: ''
use_operator: false
operator: ''
operator_limit_selection: false
operator_list: { }
identifier: ''
required: false
remember: false
multiple: false
remember_roles:
authenticated: authenticated
reduce: false
is_grouped: false
group_info:
label: ''
description: ''
identifier: ''
optional: true
widget: select
multiple: false
remember: false
default_group: All
default_group_multiple: { }
group_items: { }
reduce_duplicates: false
vid: type_de_projet
type: select
hierarchy: false
limit: true
error_message: true
save_lineage: false
force_deepest: false
parent: null
level_labels: ''
hierarchy_depth: 0
required_depth: 0
none_label: '- Please select -'
allow_unpublished: false
style:
type: default
row:
type: 'entity:node'
options:
view_mode: teaser
query:
type: views_query
options:
query_comment: ''
disable_sql_rewrite: false
distinct: false
replica: false
query_tags: { }
relationships: { }
header: { }
footer: { }
display_extenders: { }
cache_metadata:
max-age: -1
contexts:
- 'languages:language_content'
- 'languages:language_interface'
- 'user.node_grants:view'
- user.permissions
tags: { }
block_1:
id: block_1
display_title: 'Colone Gauche'
display_plugin: block
position: 2
display_options:
filters:
status:
id: status
table: node_field_data
field: status
entity_type: node
entity_field: status
plugin_id: boolean
value: '1'
group: 1
expose:
operator: ''
field_type_de_projet_target_id:
id: field_type_de_projet_target_id
table: node__field_type_de_projet
field: field_type_de_projet_target_id
relationship: none
group_type: group
admin_label: ''
plugin_id: taxonomy_index_tid
operator: or
value:
11: 11
1: 1
2: 2
group: 1
exposed: false
expose:
operator_id: ''
label: ''
description: ''
use_operator: false
operator: ''
operator_limit_selection: false
operator_list: { }
identifier: ''
required: false
remember: false
multiple: false
remember_roles:
authenticated: authenticated
reduce: false
is_grouped: false
group_info:
label: ''
description: ''
identifier: ''
optional: true
widget: select
multiple: false
remember: false
default_group: All
default_group_multiple: { }
group_items: { }
reduce_duplicates: false
vid: type_de_projet
type: select
hierarchy: false
limit: true
error_message: true
save_lineage: false
force_deepest: false
parent: null
level_labels: ''
hierarchy_depth: 0
required_depth: 0
none_label: '- Please select -'
allow_unpublished: false
filter_groups:
operator: AND
groups:
1: AND
defaults:
filters: false
filter_groups: false
display_description: ''
display_extenders: { }
block_description: 'home gauche'
cache_metadata:
max-age: -1
contexts:
- 'languages:language_content'
- 'languages:language_interface'
- 'user.node_grants:view'
- user.permissions
tags: { }
block_2:
id: block_2
display_title: 'Colone Droite'
display_plugin: block
position: 2
display_options:
filters:
status:
id: status
table: node_field_data
field: status
entity_type: node
entity_field: status
plugin_id: boolean
value: '1'
group: 1
expose:
operator: ''
field_type_de_projet_target_id:
id: field_type_de_projet_target_id
table: node__field_type_de_projet
field: field_type_de_projet_target_id
relationship: none
group_type: group
admin_label: ''
plugin_id: taxonomy_index_tid
operator: or
value:
10: 10
7: 7
6: 6
4: 4
3: 3
5: 5
group: 1
exposed: false
expose:
operator_id: ''
label: ''
description: ''
use_operator: false
operator: ''
operator_limit_selection: false
operator_list: { }
identifier: ''
required: false
remember: false
multiple: false
remember_roles:
authenticated: authenticated
reduce: false
is_grouped: false
group_info:
label: ''
description: ''
identifier: ''
optional: true
widget: select
multiple: false
remember: false
default_group: All
default_group_multiple: { }
group_items: { }
reduce_duplicates: false
vid: type_de_projet
type: select
hierarchy: false
limit: true
error_message: true
save_lineage: false
force_deepest: false
parent: null
level_labels: ''
hierarchy_depth: 0
required_depth: 0
none_label: '- Please select -'
allow_unpublished: false
field_type_de_projet_target_id_1:
id: field_type_de_projet_target_id_1
table: node__field_type_de_projet
field: field_type_de_projet_target_id
relationship: none
group_type: group
admin_label: ''
plugin_id: taxonomy_index_tid
operator: not
value:
11: 11
1: 1
2: 2
group: 1
exposed: false
expose:
operator_id: ''
label: ''
description: ''
use_operator: false
operator: ''
operator_limit_selection: false
operator_list: { }
identifier: ''
required: false
remember: false
multiple: false
remember_roles:
authenticated: authenticated
reduce: false
is_grouped: false
group_info:
label: ''
description: ''
identifier: ''
optional: true
widget: select
multiple: false
remember: false
default_group: All
default_group_multiple: { }
group_items: { }
reduce_duplicates: false
vid: type_de_projet
type: select
hierarchy: false
limit: true
error_message: true
save_lineage: false
force_deepest: false
parent: null
level_labels: ''
hierarchy_depth: 0
required_depth: 0
none_label: '- Please select -'
allow_unpublished: false
filter_groups:
operator: AND
groups:
1: AND
defaults:
filters: false
filter_groups: false
display_description: ''
display_extenders: { }
block_description: 'home droite'
cache_metadata:
max-age: -1
contexts:
- 'languages:language_content'
- 'languages:language_interface'
- 'user.node_grants:view'
- user.permissions
tags: { }
+675
View File
@@ -0,0 +1,675 @@
uuid: 9a4699d7-3273-4b1b-86d8-4162478dcbe8
langcode: fr
status: true
dependencies:
module:
- link
- redirect
- user
_core:
default_config_hash: DexqLBdD7TB7piALIPEvcHI9NvY1BNibYaTSF-aJWUw
id: redirect
label: Rediriger
module: views
description: 'List of redirects'
tag: ''
base_table: redirect
base_field: rid
display:
default:
id: default
display_title: Principal
display_plugin: default
position: 0
display_options:
title: Rediriger
fields:
redirect_bulk_form:
id: redirect_bulk_form
table: redirect
field: redirect_bulk_form
relationship: none
group_type: group
admin_label: ''
entity_type: redirect
plugin_id: redirect_bulk_form
label: ''
exclude: false
alter:
alter_text: false
text: ''
make_link: false
path: ''
absolute: false
external: false
replace_spaces: false
path_case: none
trim_whitespace: false
alt: ''
rel: ''
link_class: ''
prefix: ''
suffix: ''
target: ''
nl2br: false
max_length: 0
word_boundary: true
ellipsis: true
more_link: false
more_link_text: ''
more_link_path: ''
strip_tags: false
trim: false
preserve_tags: ''
html: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: false
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
action_title: 'With selection'
include_exclude: exclude
selected_actions: { }
redirect_source__path:
id: redirect_source__path
table: redirect
field: redirect_source__path
relationship: none
group_type: group
admin_label: ''
entity_type: redirect
entity_field: redirect_source
plugin_id: field
label: 'A partir de'
exclude: false
alter:
alter_text: false
text: ''
make_link: false
path: ''
absolute: false
external: false
replace_spaces: false
path_case: none
trim_whitespace: false
alt: ''
rel: ''
link_class: ''
prefix: ''
suffix: ''
target: ''
nl2br: false
max_length: 0
word_boundary: true
ellipsis: true
more_link: false
more_link_text: ''
more_link_path: ''
strip_tags: false
trim: false
preserve_tags: ''
html: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: true
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
click_sort_column: path
type: redirect_source
settings: { }
group_column: ''
group_columns: { }
group_rows: true
delta_limit: 0
delta_offset: 0
delta_reversed: false
delta_first_last: false
multi_type: separator
separator: ', '
field_api_classes: false
redirect_redirect__uri:
id: redirect_redirect__uri
table: redirect
field: redirect_redirect__uri
entity_type: redirect
entity_field: redirect_redirect
plugin_id: field
enabled:
id: enabled
table: redirect
field: enabled
relationship: none
group_type: group
admin_label: ''
entity_type: redirect
entity_field: enabled
plugin_id: field
label: Activé
exclude: false
alter:
alter_text: false
text: ''
make_link: false
path: ''
absolute: false
external: false
replace_spaces: false
path_case: none
trim_whitespace: false
alt: ''
rel: ''
link_class: ''
prefix: ''
suffix: ''
target: ''
nl2br: false
max_length: 0
word_boundary: true
ellipsis: true
more_link: false
more_link_text: ''
more_link_path: ''
strip_tags: false
trim: false
preserve_tags: ''
html: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: true
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
click_sort_column: value
type: boolean
settings:
format: enabled-disabled
format_custom_false: ''
format_custom_true: ''
group_column: value
group_columns: { }
group_rows: true
delta_limit: 0
delta_offset: 0
delta_reversed: false
delta_first_last: false
multi_type: separator
separator: ', '
field_api_classes: false
status_code:
id: status_code
table: redirect
field: status_code
entity_type: redirect
entity_field: status_code
plugin_id: field
language:
id: language
table: redirect
field: language
entity_type: redirect
entity_field: language
plugin_id: field
created:
id: created
table: redirect
field: created
relationship: none
group_type: group
admin_label: ''
entity_type: redirect
entity_field: created
plugin_id: date
label: Créé
exclude: false
alter:
alter_text: false
text: ''
make_link: false
path: ''
absolute: false
external: false
replace_spaces: false
path_case: none
trim_whitespace: false
alt: ''
rel: ''
link_class: ''
prefix: ''
suffix: ''
target: ''
nl2br: false
max_length: 0
word_boundary: true
ellipsis: true
more_link: false
more_link_text: ''
more_link_path: ''
strip_tags: false
trim: false
preserve_tags: ''
html: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: true
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
date_format: fallback
custom_date_format: ''
timezone: ''
operations:
id: operations
table: redirect
field: operations
entity_type: redirect
plugin_id: entity_operations
pager:
type: full
options:
offset: 0
pagination_heading_level: h4
items_per_page: 50
total_pages: null
id: 0
tags:
next: 'next '
previous: ' previous'
first: '« first'
last: 'last »'
expose:
items_per_page: false
items_per_page_label: 'Éléments par page'
items_per_page_options: '5, 10, 25, 50'
items_per_page_options_all: false
items_per_page_options_all_label: '- Tout -'
offset: false
offset_label: Décalage
quantity: 9
exposed_form:
type: basic
options:
submit_button: Filtrer
reset_button: true
reset_button_label: Réinitialiser
exposed_sorts_label: 'Trier par'
expose_sort_order: true
sort_asc_label: Asc
sort_desc_label: Desc
access:
type: perm
options:
perm: 'administer redirects'
cache:
type: tag
options: { }
empty:
area_text_custom:
id: area_text_custom
table: views
field: area_text_custom
relationship: none
group_type: group
admin_label: ''
plugin_id: text_custom
empty: true
content: 'There is no redirect yet.'
tokenize: false
sorts: { }
arguments: { }
filters:
redirect_source__path:
id: redirect_source__path
table: redirect
field: redirect_source__path
relationship: none
group_type: group
admin_label: ''
entity_type: redirect
entity_field: redirect_source
plugin_id: string
operator: contains
value: ''
group: 1
exposed: true
expose:
operator_id: redirect_source__path_op
label: 'A partir de'
description: ''
use_operator: false
operator: redirect_source__path_op
operator_limit_selection: false
operator_list: { }
identifier: redirect_source__path
required: false
remember: false
multiple: false
remember_roles:
authenticated: authenticated
is_grouped: false
group_info:
label: ''
description: ''
identifier: ''
optional: true
widget: select
multiple: false
remember: false
default_group: All
default_group_multiple: { }
group_items: { }
redirect_redirect__uri:
id: redirect_redirect__uri
table: redirect
field: redirect_redirect__uri
relationship: none
group_type: group
admin_label: ''
entity_type: redirect
entity_field: redirect_redirect
plugin_id: string
operator: contains
value: ''
group: 1
exposed: true
expose:
operator_id: redirect_redirect__uri_op
label: À
description: ''
use_operator: false
operator: redirect_redirect__uri_op
operator_limit_selection: false
operator_list: { }
identifier: redirect_redirect__uri
required: false
remember: false
multiple: false
remember_roles:
authenticated: authenticated
is_grouped: false
group_info:
label: ''
description: ''
identifier: ''
optional: true
widget: select
multiple: false
remember: false
default_group: All
default_group_multiple: { }
group_items: { }
status_code:
id: status_code
table: redirect
field: status_code
relationship: none
group_type: group
admin_label: ''
entity_type: redirect
entity_field: status_code
plugin_id: numeric
operator: '='
value:
min: ''
max: ''
value: ''
group: 1
exposed: true
expose:
operator_id: status_code_op
label: 'Code du statut'
description: ''
use_operator: false
operator: status_code_op
operator_limit_selection: false
operator_list: { }
identifier: status_code
required: false
remember: false
multiple: false
remember_roles:
authenticated: authenticated
is_grouped: true
group_info:
label: 'Code du statut'
description: ''
identifier: status_code
optional: true
widget: select
multiple: false
remember: false
default_group: All
default_group_multiple: { }
group_items:
1:
title: '300 Choix Multiples'
operator: '='
value:
min: ''
max: ''
value: '300'
2:
title: '301 Redirection Permanente'
operator: '='
value:
min: ''
max: ''
value: '301'
3:
title: '302 Trouvé'
operator: '='
value:
min: ''
max: ''
value: '302'
4:
title: '303 Voir Ailleurs'
operator: '='
value:
min: ''
max: ''
value: '303'
5:
title: '304 Non Modifié'
operator: '='
value:
min: ''
max: ''
value: '304'
6:
title: '305 Utilise un Proxy'
operator: '='
value:
min: ''
max: ''
value: '305'
7:
title: '307 Redirection Temporaire'
operator: '='
value:
min: ''
max: ''
value: '307'
language:
id: language
table: redirect
field: language
relationship: none
group_type: group
admin_label: ''
entity_type: redirect
entity_field: language
plugin_id: language
operator: in
value: { }
group: 1
exposed: true
expose:
operator_id: language_op
label: 'Langue initiale'
description: ''
use_operator: false
operator: language_op
operator_limit_selection: false
operator_list: { }
identifier: language
required: false
remember: false
multiple: false
remember_roles:
authenticated: authenticated
reduce: false
is_grouped: false
group_info:
label: ''
description: ''
identifier: ''
optional: true
widget: select
multiple: false
remember: false
default_group: All
default_group_multiple: { }
group_items: { }
filter_groups:
operator: AND
groups:
1: AND
style:
type: table
options:
grouping: { }
row_class: ''
default_row_class: true
columns:
redirect_source__path: redirect_source__path
redirect_redirect__uri: redirect_redirect__uri
enabled: enabled
status_code: status_code
language: language
created: created
operations: operations
default: created
info:
redirect_source__path:
sortable: true
default_sort_order: asc
align: ''
separator: ''
empty_column: false
responsive: ''
redirect_redirect__uri:
sortable: true
default_sort_order: asc
align: ''
separator: ''
empty_column: false
responsive: ''
enabled:
sortable: true
default_sort_order: asc
align: ''
separator: ''
empty_column: false
responsive: ''
status_code:
sortable: true
default_sort_order: asc
align: ''
separator: ''
empty_column: false
responsive: ''
language:
sortable: true
default_sort_order: asc
align: ''
separator: ''
empty_column: false
responsive: ''
created:
sortable: true
default_sort_order: asc
align: ''
separator: ''
empty_column: false
responsive: ''
operations:
sortable: false
default_sort_order: asc
align: ''
separator: ''
empty_column: false
responsive: ''
override: true
sticky: false
summary: ''
empty_table: false
caption: ''
description: ''
row:
type: fields
query:
type: views_query
options:
query_comment: ''
disable_sql_rewrite: false
distinct: false
replica: false
query_tags: { }
relationships: { }
header: { }
footer: { }
display_extenders: { }
cache_metadata:
max-age: 0
contexts:
- 'languages:language_content'
- 'languages:language_interface'
- url
- url.query_args
- user.permissions
tags: { }
cacheable: false
page_1:
id: page_1
display_title: Page
display_plugin: page
position: 1
display_options:
display_extenders: { }
path: admin/config/search/redirect
cache_metadata:
max-age: 0
contexts:
- 'languages:language_content'
- 'languages:language_interface'
- url
- url.query_args
- user.permissions
tags: { }
cacheable: false
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,166 @@
langcode: fr
status: true
dependencies:
- migrate_google_sheets
- migrate_plus
id: projet_node
label: 'Import des projets depuis Google Sheets'
migration_group: le_shed
source:
plugin: projet_node
data_fetcher_plugin: http
data_parser_plugin: google_sheets
constants:
# Trailing slash: file_import keeps the original filename and copies the
# image into this directory.
images_destination: 'public://projets/'
# The feed file for the spreadsheet. The Google Spreadsheet should be either “Public” or set to “Anyone with link can
# view” in order for the feed to work.
# Template: 'https://sheets.googleapis.com/v4/spreadsheets/<SHEET>/values/<TAB>?key=<KEY>'
# La clé API n'est volontairement pas dans l'URL : le data_parser
# google_sheets ajoute automatiquement la clé stockée en base
# (config migrate_google_sheets.settings, réglée via l'UI du module).
urls: 'https://sheets.googleapis.com/v4/spreadsheets/1561Gv1C-IlCTmg64Z8HnznvOeLbvwhA_k1l6aq95zFA/values/Projets'
# Under 'fields', we list the data items to be imported. The first level keys
# are the source field names we want to populate (the names to be used as
# sources in the process configuration below). For each field we're importing,
# we provide a label (optional - this is for display in migration tools) and
# an selector (xpath) for retrieving that value. It's important to note that this xpath
# is relative to the elements retrieved by item_selector.
# For Google Spreadsheet XML feeds the actual columns are named with gsx: followed by the cleaned column name (lower,
# limited punctuation, etc).destination:
fields:
-
name: id
label: 'Unique identifier'
selector: 'UID'
-
name: title
label: 'Title'
selector: 'Nom du projet'
-
name: date_start
label: 'Date start'
selector: 'Date de début de projet'
-
name: date_end
label: 'Date end'
selector: 'Date de fin de projet'
-
name: projet_type
label: 'Type de projet'
selector: 'Type de projet'
-
name: curator
label: 'Commissaria'
selector: 'invité/accompagné/curaté par'
-
name: artistes
label: 'Artistes invité·es'
selector: 'Artistes invité·es'
-
name: partners
label: 'Partenaires associés'
selector: 'Partenaires associés'
# Under 'ids', we identify source fields populated above which will uniquely
# identify each imported item. The 'type' makes sure the migration map table
# uses the proper schema type for stored the IDs.
ids:
id:
type: integer
process:
type:
plugin: default_value
default_value: projet
title:
plugin: skip_on_empty
method: row
source: title
message: 'Projet ignoré : « Nom du projet » vide.'
body/value: body_html
body/format:
plugin: default_value
default_value: wysiwyg
field_images:
plugin: sub_process
source: images
process:
target_id:
plugin: image_resize_import
source: uri
destination: destination
max_width: 1920
max_height: 1920
reuse: true
skip_on_missing_source: true
alt: alt
image_field_caption/value: caption
image_field_caption/format:
plugin: default_value
default_value: wysiwyg
field_fichiers:
plugin: sub_process
source: pdfs
process:
target_id:
plugin: file_import
source: uri
destination: destination
reuse: true
skip_on_missing_source: true
description: description
field_dates/value:
plugin: leshed_date
source: date_start
field_dates/end_value:
plugin: leshed_date
source: date_end
end_of_year: true
fallback_source: date_start
field_type_de_projet:
-
plugin: skip_on_empty
method: process
source: projet_type
-
plugin: explode
delimiter: ','
-
plugin: callback
callable: trim
-
plugin: entity_generate
entity_type: taxonomy_term
bundle_key: vid
bundle: type_de_projet
value_key: name
field_invite_accompagne_curate_p:
-
plugin: skip_on_empty
method: process
source: curator
-
plugin: personne_generate
type_de_personne: curateur
field_artiste_s_invite_s:
-
plugin: skip_on_empty
method: process
source: artistes
-
plugin: personne_generate
type_de_personne: artiste
field_partenaire_s:
-
plugin: skip_on_empty
method: process
source: partners
-
plugin: partenaire_generate
uid:
plugin: default_value
default_value: 3
migration_dependencies: { }
destination:
plugin: entity:node
@@ -0,0 +1,8 @@
status: true
dependencies: { }
id: le_shed
label: 'Le shed'
description: ''
source_type: 'google doc sheets'
module: null
shared_configuration: null
@@ -0,0 +1,21 @@
type: module
name: Migrate Le Shed
description: 'Importing contents from google sheet.'
package: Migration
core_version_requirement: '^10.5 || ^11'
hidden: false
dependencies:
- migrate_plus:migrate_plus
- migrate_tools:migrate_tools
- migrate_google_sheets:migrate_google_sheets
- drupal:image
- drupal:text
- drupal:options
- drupal:taxonomy
# Information added by Drupal.org packaging script on 2026-01-23
version: '0.0.1'
project: 'leshed'
# https://sheets.googleapis.com/v4/spreadsheets/1561Gv1C-IlCTmg64Z8HnznvOeLbvwhA_k1l6aq95zFA/values/Projets/?key=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
@@ -0,0 +1,153 @@
<?php
declare(strict_types=1);
namespace Drupal\migrate_leshed\Plugin\migrate\process;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\File\FileExists;
use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\migrate\Attribute\MigrateProcess;
use Drupal\migrate\MigrateExecutableInterface;
use Drupal\migrate\MigrateSkipRowException;
use Drupal\migrate\ProcessPluginBase;
use Drupal\migrate\Row;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Copies a local file into the file system as a managed File entity.
*
* The source value is a readable file path/URI. The file is copied into the
* destination directory and registered as a managed file; the file id is
* returned.
*
* Configuration:
* - destination: (required) destination directory URI, e.g. 'public://2026-08/'.
* - uid: owner uid of the created file (default 0).
* - reuse: reuse an existing managed file at the destination URI (default TRUE).
* - skip_on_missing_source: skip the value when the source is missing
* (default TRUE) instead of failing.
*
* @code
* field_fichiers/target_id:
* plugin: file_import
* source: uri
* destination: destination
* @endcode
*/
#[MigrateProcess(id: 'file_import')]
final class FileImport extends ProcessPluginBase implements ContainerFactoryPluginInterface {
/**
* Per-run cache of already resolved destination URIs to file ids.
*
* @var array<string, int>
*/
private array $fidCache = [];
public function __construct(
array $configuration,
$plugin_id,
$plugin_definition,
private readonly FileSystemInterface $fileSystem,
private readonly EntityTypeManagerInterface $entityTypeManager,
) {
$configuration += [
'uid' => 0,
'reuse' => TRUE,
'skip_on_missing_source' => TRUE,
];
parent::__construct($configuration, $plugin_id, $plugin_definition);
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): self {
return new self(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('file_system'),
$container->get('entity_type.manager'),
);
}
/**
* {@inheritdoc}
*/
public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property): ?int {
$source = is_string($value) ? trim($value) : '';
if ($source === '') {
return NULL;
}
if (!is_file($source) && !is_file($this->fileSystem->realpath($source) ?: $source)) {
return $this->skipFile($migrate_executable, "Source file '$source' is missing.");
}
$directory = $this->getPropertyValue($this->configuration['destination'], $row);
if (!is_string($directory) || $directory === '') {
return $this->skipFile($migrate_executable, "Missing 'destination' for file '$source'.");
}
$directory = rtrim($directory, '/') . '/';
$destination = $directory . $this->fileSystem->basename($source);
// Reuse an already imported file to keep the migration idempotent.
if ($this->configuration['reuse']) {
if (isset($this->fidCache[$destination])) {
return $this->fidCache[$destination];
}
$existing = $this->entityTypeManager->getStorage('file')
->loadByProperties(['uri' => $destination]);
if ($existing) {
return $this->fidCache[$destination] = (int) reset($existing)->id();
}
}
try {
if (!$this->fileSystem->prepareDirectory($directory, FileSystemInterface::CREATE_DIRECTORY | FileSystemInterface::MODIFY_PERMISSIONS)) {
return $this->skipFile($migrate_executable, "Cannot prepare directory '$directory'.");
}
$this->fileSystem->copy($source, $destination, FileExists::Replace);
$file = $this->entityTypeManager->getStorage('file')->create([
'uri' => $destination,
'uid' => (int) $this->getPropertyValue($this->configuration['uid'], $row),
'status' => 1,
]);
$file->save();
}
catch (\Throwable $e) {
return $this->skipFile($migrate_executable, "Import of file '$source' failed: " . $e->getMessage());
}
return $this->fidCache[$destination] = (int) $file->id();
}
/**
* Logs a message and skips the current file item.
*
* @throws \Drupal\migrate\MigrateSkipRowException
*/
private function skipFile(MigrateExecutableInterface $migrate_executable, string $message): never {
$migrate_executable->saveMessage($message);
throw new MigrateSkipRowException($message, FALSE);
}
/**
* Resolves a configuration value, supporting destination (@) references.
*/
private function getPropertyValue(mixed $key, Row $row): mixed {
if (is_string($key) && str_starts_with($key, '@')) {
return $row->get(substr($key, 1));
}
if (is_string($key) && $row->hasSourceProperty($key)) {
return $row->getSourceProperty($key);
}
return $key;
}
}
@@ -0,0 +1,179 @@
<?php
declare(strict_types=1);
namespace Drupal\migrate_leshed\Plugin\migrate\process;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\Image\ImageFactory;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\migrate\Attribute\MigrateProcess;
use Drupal\migrate\MigrateExecutableInterface;
use Drupal\migrate\MigrateSkipRowException;
use Drupal\migrate\ProcessPluginBase;
use Drupal\migrate\Row;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Copies a local image, downscaling it to a web-friendly size, as a File.
*
* The source value is a readable file path/URI. The image is scaled down
* (aspect ratio preserved, never upscaled) to fit within max_width x
* max_height, saved into the destination directory and registered as a
* managed file. Returns the file id.
*
* Configuration:
* - destination: (required) destination directory URI, e.g. 'public://projets/1/'.
* - max_width: maximum width in pixels (default 1920).
* - max_height: maximum height in pixels (default 1920).
* - uid: owner uid of the created file (default 0).
* - reuse: reuse an existing managed file at the destination URI (default TRUE).
* - skip_on_missing_source: skip the value when the source is missing
* (default TRUE) instead of failing.
*
* @code
* field_images/target_id:
* plugin: image_resize_import
* source: uri
* destination: destination
* max_width: 1920
* max_height: 1920
* @endcode
*/
#[MigrateProcess(id: 'image_resize_import')]
final class ImageResizeImport extends ProcessPluginBase implements ContainerFactoryPluginInterface {
/**
* Per-run cache of already resolved destination URIs to file ids.
*
* @var array<string, int>
*/
private array $fidCache = [];
public function __construct(
array $configuration,
$plugin_id,
$plugin_definition,
private readonly FileSystemInterface $fileSystem,
private readonly ImageFactory $imageFactory,
private readonly EntityTypeManagerInterface $entityTypeManager,
) {
$configuration += [
'max_width' => 1920,
'max_height' => 1920,
'uid' => 0,
'reuse' => TRUE,
'skip_on_missing_source' => TRUE,
];
parent::__construct($configuration, $plugin_id, $plugin_definition);
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): self {
return new self(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('file_system'),
$container->get('image.factory'),
$container->get('entity_type.manager'),
);
}
/**
* {@inheritdoc}
*/
public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property): ?int {
$source = is_string($value) ? trim($value) : '';
if ($source === '') {
return NULL;
}
// Stream wrappers support is_file() directly; realpath() is only needed as
// a fallback for schemes it cannot resolve.
if (!is_file($source) && !is_file($this->fileSystem->realpath($source) ?: $source)) {
// Skipping throws MigrateSkipRowException because, inside a sub_process,
// only that exception drops the whole image item cleanly (SubProcess
// catches it and `continue`s); MigrateSkipProcessException would instead
// leave a broken delta with alt/caption but no target_id.
return $this->skipImage($migrate_executable, "Source image '$source' is missing.");
}
$directory = $this->getPropertyValue($this->configuration['destination'], $row);
if (!is_string($directory) || $directory === '') {
return $this->skipImage($migrate_executable, "Missing 'destination' for image '$source'.");
}
$directory = rtrim($directory, '/') . '/';
$destination = $directory . $this->fileSystem->basename($source);
// Reuse an already imported file (idempotency), memoized for the run.
if ($this->configuration['reuse']) {
if (isset($this->fidCache[$destination])) {
return $this->fidCache[$destination];
}
$existing = $this->entityTypeManager->getStorage('file')
->loadByProperties(['uri' => $destination]);
if ($existing) {
return $this->fidCache[$destination] = (int) reset($existing)->id();
}
}
// Any infrastructure failure only drops the current image (logged), so a
// single unreadable/oversized file never aborts the whole batch.
try {
if (!$this->fileSystem->prepareDirectory($directory, FileSystemInterface::CREATE_DIRECTORY | FileSystemInterface::MODIFY_PERMISSIONS)) {
return $this->skipImage($migrate_executable, "Cannot prepare directory '$directory'.");
}
$image = $this->imageFactory->get($source);
if (!$image->isValid()) {
return $this->skipImage($migrate_executable, "Source '$source' is not a valid image.");
}
// Downscale only: scale() never upscales when upscale is FALSE.
$image->scale((int) $this->configuration['max_width'], (int) $this->configuration['max_height']);
if (!$image->save($destination)) {
return $this->skipImage($migrate_executable, "Failed to write resized image to '$destination'.");
}
$file = $this->entityTypeManager->getStorage('file')->create([
'uri' => $destination,
'uid' => (int) $this->getPropertyValue($this->configuration['uid'], $row),
'status' => 1,
]);
$file->save();
}
catch (\Throwable $e) {
return $this->skipImage($migrate_executable, "Import of image '$source' failed: " . $e->getMessage());
}
return $this->fidCache[$destination] = (int) $file->id();
}
/**
* Logs a message and skips the current image item.
*
* @throws \Drupal\migrate\MigrateSkipRowException
*/
private function skipImage(MigrateExecutableInterface $migrate_executable, string $message): never {
$migrate_executable->saveMessage($message);
throw new MigrateSkipRowException($message, FALSE);
}
/**
* Resolves a configuration value, supporting destination (@) references.
*/
private function getPropertyValue(mixed $key, Row $row): mixed {
if (is_string($key) && str_starts_with($key, '@')) {
return $row->get(substr($key, 1));
}
if (is_string($key) && $row->hasSourceProperty($key)) {
return $row->getSourceProperty($key);
}
return $key;
}
}
@@ -0,0 +1,77 @@
<?php
declare(strict_types=1);
namespace Drupal\migrate_leshed\Plugin\migrate\process;
use Drupal\migrate\Attribute\MigrateProcess;
use Drupal\migrate\MigrateExecutableInterface;
use Drupal\migrate\ProcessPluginBase;
use Drupal\migrate\Row;
/**
* Converts Google Sheet project dates to 'Y-m-d'.
*
* Accepted inputs:
* - 'd/m/Y' (e.g. "19/09/2015"),
* - a bare year (e.g. "2016"), converted to the first day of the year, or
* to the last day when 'end_of_year: true' is set in the configuration.
*
* When the value is empty or invalid and 'fallback_source' is set in the
* configuration, the named source property is converted instead. This is
* used to default the end date to the start date, since the daterange
* field requires a non-null end value.
*
* Invalid or unparseable values return NULL and are logged in the
* migration messages instead of failing the whole row.
*/
#[MigrateProcess(id: 'leshed_date')]
final class LeshedDate extends ProcessPluginBase {
/**
* {@inheritdoc}
*/
public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property): ?string {
$result = $this->convert($value, $migrate_executable, $destination_property);
if ($result === NULL && !empty($this->configuration['fallback_source'])) {
$fallback = $row->getSourceProperty($this->configuration['fallback_source']);
$result = $this->convert($fallback, $migrate_executable, $destination_property);
}
return $result;
}
/**
* Converts a single raw value to 'Y-m-d', or NULL if not parseable.
*/
private function convert(mixed $value, MigrateExecutableInterface $migrate_executable, string $destination_property): ?string {
if (!is_string($value)) {
return NULL;
}
$value = trim($value);
if ($value === '') {
return NULL;
}
// Bare year: "2016".
if (preg_match('/^\d{4}$/', $value)) {
return empty($this->configuration['end_of_year'])
? $value . '-01-01'
: $value . '-12-31';
}
// Full date: "19/09/2015".
$date = \DateTime::createFromFormat('!d/m/Y', $value);
$errors = \DateTime::getLastErrors();
if ($date === FALSE || ($errors && ($errors['warning_count'] || $errors['error_count']))) {
$migrate_executable->saveMessage(
sprintf('Date invalide "%s" pour la propriété "%s".', $value, $destination_property)
);
return NULL;
}
return $date->format('Y-m-d');
}
}
@@ -0,0 +1,100 @@
<?php
declare(strict_types=1);
namespace Drupal\migrate_leshed\Plugin\migrate\process;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\migrate\Attribute\MigrateProcess;
use Drupal\migrate\MigrateExecutableInterface;
use Drupal\migrate\ProcessPluginBase;
use Drupal\migrate\Row;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Finds or creates 'partenaire' nodes from a comma-separated list of names.
*
* Input: a string such as "Port Autonome de Rouen, Labelle 69". Names are
* split on commas and trimmed. Empty values and placeholders ("/") are
* skipped. An existing node with the same title is reused, otherwise it is
* created. Returns an array of node IDs.
*/
#[MigrateProcess(id: 'partenaire_generate', handle_multiples: TRUE)]
final class PartenaireGenerate extends ProcessPluginBase implements ContainerFactoryPluginInterface {
/**
* Placeholder values that must not create a partenaire node.
*/
private const IGNORED_VALUES = ['/', '?', '-'];
public function __construct(
array $configuration,
$plugin_id,
$plugin_definition,
private readonly EntityTypeManagerInterface $entityTypeManager,
) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): self {
return new self(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('entity_type.manager'),
);
}
/**
* {@inheritdoc}
*/
public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property): array {
if (!is_string($value) || trim($value) === '') {
return [];
}
$names = array_filter(array_map('trim', explode(',', $value)));
$nids = [];
foreach ($names as $name) {
if (in_array($name, self::IGNORED_VALUES, TRUE)) {
continue;
}
$nids[] = $this->getOrCreatePartenaire($name);
}
return $nids;
}
/**
* Returns the nid of the matching partenaire node, creating it if needed.
*/
private function getOrCreatePartenaire(string $title): int {
$storage = $this->entityTypeManager->getStorage('node');
$ids = $storage->getQuery()
->condition('type', 'partenaire')
->condition('title', $title)
->accessCheck(FALSE)
->range(0, 1)
->execute();
if ($ids) {
return (int) reset($ids);
}
$node = $storage->create([
'type' => 'partenaire',
'title' => $title,
'status' => 1,
'uid' => $this->configuration['author_uid'] ?? 3,
]);
$node->save();
return (int) $node->id();
}
}
@@ -0,0 +1,183 @@
<?php
declare(strict_types=1);
namespace Drupal\migrate_leshed\Plugin\migrate\process;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\migrate\Attribute\MigrateProcess;
use Drupal\migrate\MigrateExecutableInterface;
use Drupal\migrate\ProcessPluginBase;
use Drupal\migrate\Row;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Finds or creates 'personne' nodes from a list of names.
*
* Input: a string such as "Julie Faitot et Jonathan Loppin" or
* "Sonja Beaudouin, Alexandre Delabrière". Names are split on "et" and
* commas. For each name, the first word is used as 'field_prenom' and the
* rest as 'field_nom'. The node title is generated from those two fields by
* the auto_entitylabel module on save. An existing node with the same nom and
* prenom is reused, otherwise it is created. Returns an array of node IDs.
*
* When 'type_de_personne' is set in the configuration, a taxonomy term with
* that name is found or created (without duplicates) in the
* 'type_de_personne' vocabulary, and referenced from the node's
* 'field_type_de_personne' field. The tag is also added to matching
* existing nodes that do not yet have it.
*/
#[MigrateProcess(id: 'personne_generate', handle_multiples: TRUE)]
final class PersonneGenerate extends ProcessPluginBase implements ContainerFactoryPluginInterface {
/**
* Placeholder values that must not create a personne node.
*/
private const IGNORED_VALUES = ['/', '?', '-'];
public function __construct(
array $configuration,
$plugin_id,
$plugin_definition,
private readonly EntityTypeManagerInterface $entityTypeManager,
) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): self {
return new self(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('entity_type.manager'),
);
}
/**
* {@inheritdoc}
*/
public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property): array {
if (!is_string($value) || trim($value) === '') {
return [];
}
// Ignore everything after "avec" (e.g. "Camille Bondon avec les élèves
// de l'école André Marie" only keeps "Camille Bondon").
$value = preg_replace('/\s+avec\s+.*$/us', '', $value);
// Normalize separators: " et " becomes a comma.
$value = preg_replace('/\s+et\s+/u', ',', $value);
$names = array_filter(array_map('trim', explode(',', $value)));
$nids = [];
foreach ($names as $name) {
if (in_array($name, self::IGNORED_VALUES, TRUE)) {
continue;
}
// First word is the first name, the rest is the last name.
$parts = preg_split('/\s+/u', $name, 2);
$prenom = $parts[0];
$nom = $parts[1] ?? '';
// Single-word name: use it as the title, without a first name.
if ($nom === '') {
$nom = $prenom;
$prenom = '';
}
$nids[] = $this->getOrCreatePersonne($nom, $prenom);
}
return $nids;
}
/**
* Returns the nid of the matching personne node, creating it if needed.
*/
private function getOrCreatePersonne(string $nom, string $prenom): int {
$storage = $this->entityTypeManager->getStorage('node');
$type_name = $this->configuration['type_de_personne'] ?? NULL;
$tid = $type_name ? $this->getOrCreateType($type_name) : NULL;
$query = $storage->getQuery()
->condition('type', 'personne')
->condition('field_nom', $nom)
->accessCheck(FALSE)
->range(0, 1);
if ($prenom !== '') {
$query->condition('field_prenom', $prenom);
}
else {
$query->notExists('field_prenom');
}
$ids = $query->execute();
if ($ids) {
$nid = (int) reset($ids);
if ($tid !== NULL) {
$this->addTypeToPersonne($nid, $tid);
}
return $nid;
}
// The node title is generated from field_prenom + field_nom by
// auto_entitylabel on save; it is intentionally not set here.
$values = [
'type' => 'personne',
'field_nom' => $nom,
'field_prenom' => $prenom === '' ? NULL : $prenom,
'status' => 1,
'uid' => $this->configuration['author_uid'] ?? 3,
];
if ($tid !== NULL) {
$values['field_type_de_personne'] = $tid;
}
$node = $storage->create($values);
$node->save();
return (int) $node->id();
}
/**
* Adds a type_de_personne term to an existing node if not already set.
*/
private function addTypeToPersonne(int $nid, int $tid): void {
$node = $this->entityTypeManager->getStorage('node')->load($nid);
if ($node === NULL) {
return;
}
$existing = array_column($node->get('field_type_de_personne')->getValue(), 'target_id');
if (!in_array((string) $tid, array_map('strval', $existing), TRUE)) {
$node->get('field_type_de_personne')->appendItem(['target_id' => $tid]);
$node->save();
}
}
/**
* Returns the tid of the matching type_de_personne term, creating it if needed.
*/
private function getOrCreateType(string $name): int {
$storage = $this->entityTypeManager->getStorage('taxonomy_term');
$ids = $storage->getQuery()
->condition('vid', 'type_de_personne')
->condition('name', $name)
->accessCheck(FALSE)
->range(0, 1)
->execute();
if ($ids) {
return (int) reset($ids);
}
$term = $storage->create([
'vid' => 'type_de_personne',
'name' => $name,
]);
$term->save();
return (int) $term->id();
}
}
@@ -0,0 +1,459 @@
<?php
declare(strict_types=1);
namespace Drupal\migrate_leshed\Plugin\migrate\source;
use Drupal\Core\Entity\EntityFieldManagerInterface;
use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\Utility\Token;
use Drupal\migrate\Attribute\MigrateSource;
use Drupal\migrate\Plugin\MigrationInterface;
use Drupal\migrate\Row;
use Drupal\migrate_plus\DataParserPluginManager;
use Drupal\migrate_plus\Plugin\migrate\source\Url;
use League\CommonMark\CommonMarkConverter;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Source plugin for projects, enriched with body and images from the drive.
*
* On top of the Google Sheets columns, this source reads the local files
* synced from the shared Google Drive (see the `sync_leshed_drive` Makefile
* target) that live under public://drive_shared_leshed. Files are grouped in
* per-project folders named "Page{UID}" (optionally suffixed, e.g.
* "Page1_Complet"), where {UID} matches the sheet UID column:
* - "{UID}_texte_*.md": the body, written in Markdown, converted to HTML.
* - "{UID}_images|Images/": one folder of images, each "{name}.jpg" paired
* with a "{name}_legende.md" caption used as the (required) alt text.
*
* These are exposed as two extra source properties consumed by the migration:
* - body_html: the converted HTML string (or NULL).
* - images: a list of ['uri' => string, 'alt' => string] items.
*
* @MigrateSource(
* id = "projet_node"
* )
*/
#[MigrateSource(id: 'projet_node')]
final class ProjetNode extends Url {
/**
* Stream wrapper URI of the synced drive folder.
*/
private const DRIVE_URI = 'public://drive_shared_leshed';
/**
* Image file extensions to import into field_images.
*/
private const IMAGE_EXTENSIONS = ['png', 'gif', 'jpg', 'jpeg', 'webp'];
/**
* File extensions to import into field_fichiers.
*/
private const FILE_EXTENSIONS = ['pdf'];
/**
* Default destination directory when no constant is configured.
*/
private const DEFAULT_IMAGE_DESTINATION = 'public://projets/';
/**
* Lazily instantiated Markdown converter.
*/
private ?CommonMarkConverter $markdownConverter = NULL;
/**
* Memoized map of UID => project folder URI, built once from the drive root.
*
* @var array<string, string>|null
*/
private ?array $folderMap = NULL;
/**
* Memoized destination directory for field_fichiers (from field settings).
*/
private ?string $filesDestination = NULL;
public function __construct(
array $configuration,
$plugin_id,
$plugin_definition,
MigrationInterface $migration,
DataParserPluginManager $parserPluginManager,
private readonly FileSystemInterface $fileSystem,
private readonly EntityFieldManagerInterface $entityFieldManager,
private readonly Token $token,
) {
parent::__construct($configuration, $plugin_id, $plugin_definition, $migration, $parserPluginManager);
}
/**
* {@inheritdoc}
*/
public static function create($container, array $configuration, $plugin_id, $plugin_definition, ?MigrationInterface $migration = NULL): self {
return new self(
$configuration,
$plugin_id,
$plugin_definition,
$migration,
$container->get('plugin.manager.migrate_plus.data_parser'),
$container->get('file_system'),
$container->get('entity_field.manager'),
$container->get('token'),
);
}
/**
* {@inheritdoc}
*/
public function fields(): array {
return parent::fields() + [
'body_html' => $this->t('Project body (HTML converted from Markdown)'),
'images' => $this->t('Project images with captions'),
'pdfs' => $this->t('Project PDF files'),
];
}
/**
* {@inheritdoc}
*/
public function getIds(): array {
return [
'id' => [
'type' => 'string',
],
];
}
/**
* {@inheritdoc}
*/
public function prepareRow(Row $row): bool {
// Let the parent (and prepare-row event subscribers) decide first: no need
// to hit the filesystem for a row that will be skipped.
if (!parent::prepareRow($row)) {
return FALSE;
}
// Always define both properties so the process pipeline never references
// an undefined source key, even when no drive folder exists yet.
$row->setSourceProperty('body_html', NULL);
$row->setSourceProperty('images', []);
$row->setSourceProperty('pdfs', []);
$uid = trim((string) $row->getSourceProperty('id'));
$folder = $uid !== '' ? $this->findProjectFolder($uid) : NULL;
if ($folder !== NULL) {
$row->setSourceProperty('body_html', $this->readBody($folder, $uid));
$row->setSourceProperty('images', $this->readImages($folder, $uid, (string) $row->getSourceProperty('title')));
$row->setSourceProperty('pdfs', $this->readPdfs($folder, $uid));
}
return TRUE;
}
/**
* Returns the public:// URI of the project folder, or NULL when missing.
*/
private function findProjectFolder(string $uid): ?string {
return $this->getFolderMap()[$uid] ?? NULL;
}
/**
* Builds (once) the UID => project folder URI map from the drive root.
*
* @return array<string, string>
* Map keyed by the numeric part of each "Page{UID}[_suffix]" folder.
*/
private function getFolderMap(): array {
if ($this->folderMap !== NULL) {
return $this->folderMap;
}
$this->folderMap = [];
$base = $this->fileSystem->realpath(self::DRIVE_URI);
if ($base === FALSE || !is_dir($base)) {
return $this->folderMap;
}
// Match "Page{N}" exactly or with a suffix ("_Complet", ...). Sorting keeps
// the resolution deterministic when e.g. "Page5" and "Page5_Complet"
// coexist (the plain, longer-suffixed folder wins consistently).
$entries = scandir($base) ?: [];
sort($entries, SORT_NATURAL | SORT_FLAG_CASE);
foreach ($entries as $entry) {
if ($entry === '.' || $entry === '..' || !is_dir($base . '/' . $entry)) {
continue;
}
if (preg_match('/^Page(\d+)(_.*)?$/', $entry, $matches)) {
$this->folderMap[$matches[1]] = self::DRIVE_URI . '/' . $entry;
}
}
return $this->folderMap;
}
/**
* Reads the Markdown body file and converts it to HTML.
*/
private function readBody(string $folderUri, string $uid): ?string {
$dir = $this->fileSystem->realpath($folderUri);
if ($dir === FALSE) {
return NULL;
}
// Prefer "{UID}_texte*.md", fall back to any Markdown file at the root.
$preferred = '/^' . preg_quote($uid, '/') . '_texte.*\.md$/i';
$entries = scandir($dir) ?: [];
sort($entries, SORT_NATURAL | SORT_FLAG_CASE);
$markdown = NULL;
foreach ($entries as $entry) {
if (!is_file($dir . '/' . $entry) || !preg_match('/\.md$/i', $entry)) {
continue;
}
if (preg_match($preferred, $entry)) {
$markdown = $dir . '/' . $entry;
break;
}
$markdown ??= $dir . '/' . $entry;
}
if ($markdown === NULL) {
return NULL;
}
$contents = file_get_contents($markdown);
if ($contents === FALSE || trim($contents) === '') {
return NULL;
}
$contents = $this->cleanBodyMarkdown($contents);
if ($contents === '') {
return NULL;
}
return (string) $this->getMarkdownConverter()->convert($contents);
}
/**
* Cleans the raw source Markdown before conversion.
*
* Removes redundant content already present elsewhere: raw HTML wrappers,
* invisible characters, and the leading title block (H1 title + H2/H3
* artists/subtitle, redundant with the sheet). Headings located *inside* the
* body (e.g. artist/curator bios) are preserved. Idempotent: running it on an
* already-cleaned text has no effect.
*/
private function cleanBodyMarkdown(string $md): string {
// 1. Strip HTML tags (<div>, <br>, </div>, ...).
$md = (string) preg_replace('/<[^>]+>/', '', $md);
// 2. Strip zero-width/BOM characters and turn NBSP into a normal space.
$md = (string) preg_replace('/[\x{200B}\x{200E}\x{200F}\x{FEFF}]/u', '', $md);
$md = (string) preg_replace('/\x{00A0}/u', ' ', $md);
// 3. Drop only the leading heading block: skip leading blank and heading
// lines until the first body line.
$lines = preg_split('/\R/u', $md) ?: [];
$i = 0;
$count = count($lines);
while ($i < $count && (trim($lines[$i]) === '' || preg_match('/^\s*#{1,6}\s/u', $lines[$i]))) {
$i++;
}
$md = implode("\n", array_slice($lines, $i));
// 4. Collapse blank lines and trim.
$md = (string) preg_replace('/\n{3,}/', "\n\n", $md);
return trim($md);
}
/**
* Builds the list of images (URI + alt) for the project.
*
* @return array<int, array{uri: string, alt: string, caption: string, destination: string}>
* The images to import.
*/
private function readImages(string $folderUri, string $uid, string $title): array {
$dir = $this->fileSystem->realpath($folderUri);
if ($dir === FALSE) {
return [];
}
// The destination is passed through each item because a sub_process row
// cannot read the parent migration's `constants`. It is namespaced per
// project (UID) so identically-named files from different projects never
// collide when file_import reuses existing files (reuse: true).
$base = $this->configuration['constants']['images_destination']
?? self::DEFAULT_IMAGE_DESTINATION;
$destination = rtrim($base, '/') . '/' . $uid . '/';
// Locate the images subfolder ("{UID}_images", case-insensitive).
$imagesDirUri = NULL;
$pattern = '/^' . preg_quote($uid, '/') . '_images$/i';
foreach (scandir($dir) ?: [] as $entry) {
if (preg_match($pattern, $entry) && is_dir($dir . '/' . $entry)) {
$imagesDirUri = $folderUri . '/' . $entry;
break;
}
}
if ($imagesDirUri === NULL) {
return [];
}
$imagesDir = $this->fileSystem->realpath($imagesDirUri);
if ($imagesDir === FALSE) {
return [];
}
$images = [];
$files = scandir($imagesDir) ?: [];
sort($files, SORT_NATURAL | SORT_FLAG_CASE);
foreach ($files as $file) {
$extension = strtolower(pathinfo($file, PATHINFO_EXTENSION));
if (!in_array($extension, self::IMAGE_EXTENSIONS, TRUE)) {
continue;
}
$name = pathinfo($file, PATHINFO_FILENAME);
$legende = $this->readCaptionFile($imagesDir . '/' . $name . '_legende.md');
// Plain text (required alt), falling back to the project title. Capped to
// the image field's alt column length (varchar 512).
$alt = $legende !== '' ? $this->toPlainText($legende) : $title;
$alt = mb_substr($alt, 0, 512);
// Rich caption (image_field_caption): Markdown converted to HTML.
$caption = $legende !== '' ? (string) $this->getMarkdownConverter()->convert($legende) : '';
$images[] = [
'uri' => $imagesDirUri . '/' . $file,
'alt' => $alt,
'caption' => $caption,
'destination' => $destination,
];
}
return $images;
}
/**
* Builds the list of PDF files for the project.
*
* Collects PDFs both at the project folder root and inside the "{UID}_pdf"
* subfolder. Each item carries the destination directory (from the
* field_fichiers settings) and a human-readable description.
*
* @return array<int, array{uri: string, destination: string, description: string}>
* The files to import.
*/
private function readPdfs(string $folderUri, string $uid): array {
$dir = $this->fileSystem->realpath($folderUri);
if ($dir === FALSE) {
return [];
}
// Directories to scan: the project folder root and its "{UID}_pdf" subfolder.
$sources = [$folderUri];
$pattern = '/^' . preg_quote($uid, '/') . '_pdf$/i';
foreach (scandir($dir) ?: [] as $entry) {
if (preg_match($pattern, $entry) && is_dir($dir . '/' . $entry)) {
$sources[] = $folderUri . '/' . $entry;
}
}
$destination = $this->filesDestination();
$pdfs = [];
foreach ($sources as $sourceUri) {
$sourceDir = $this->fileSystem->realpath($sourceUri);
if ($sourceDir === FALSE) {
continue;
}
$files = scandir($sourceDir) ?: [];
sort($files, SORT_NATURAL | SORT_FLAG_CASE);
foreach ($files as $file) {
if (!is_file($sourceDir . '/' . $file)) {
continue;
}
if (!in_array(strtolower(pathinfo($file, PATHINFO_EXTENSION)), self::FILE_EXTENSIONS, TRUE)) {
continue;
}
$pdfs[] = [
'uri' => $sourceUri . '/' . $file,
'destination' => $destination,
'description' => $this->humanizeFilename($file),
];
}
}
return $pdfs;
}
/**
* Resolves (once) the field_fichiers destination directory from its settings.
*/
private function filesDestination(): string {
if ($this->filesDestination !== NULL) {
return $this->filesDestination;
}
$scheme = 'public';
$directory = '';
$definitions = $this->entityFieldManager->getFieldDefinitions('node', 'projet');
$field = $definitions['field_fichiers'] ?? NULL;
if ($field !== NULL) {
$scheme = $field->getFieldStorageDefinition()->getSetting('uri_scheme') ?: 'public';
$directory = (string) $this->token->replace((string) $field->getSetting('file_directory'));
}
return $this->filesDestination = $scheme . '://' . trim($directory, '/') . '/';
}
/**
* Turns a file name into a readable label (drops UID prefix and extension).
*/
private function humanizeFilename(string $file): string {
$name = pathinfo($file, PATHINFO_FILENAME);
// Drop a leading "{UID}_" numeric prefix.
$name = preg_replace('/^\d+[_-]/', '', $name) ?? $name;
// Separators to spaces, collapse, trim.
$name = (string) preg_replace('/[_\-]+/u', ' ', $name);
return trim((string) preg_replace('/\s+/u', ' ', $name));
}
/**
* Returns the raw contents of a caption (legende) file, or '' when missing.
*/
private function readCaptionFile(string $captionPath): string {
if (!is_file($captionPath)) {
return '';
}
$contents = file_get_contents($captionPath);
if ($contents === FALSE) {
return '';
}
// Drive exports are occasionally Windows-1252 rather than UTF-8.
if (!mb_check_encoding($contents, 'UTF-8')) {
$contents = mb_convert_encoding($contents, 'UTF-8', 'Windows-1252');
}
return trim($contents);
}
/**
* Reduces Markdown/HTML text to a single trimmed plain-text line (for alt).
*/
private function toPlainText(string $text): string {
$text = html_entity_decode(strip_tags($text), ENT_QUOTES | ENT_HTML5, 'UTF-8');
// Drop Markdown emphasis/heading markers left after tag stripping.
$text = preg_replace('/[*_#`>]+/u', '', $text) ?? $text;
return trim((string) preg_replace('/\s+/u', ' ', $text));
}
/**
* Returns the shared Markdown converter.
*
* Raw HTML embedded in the source Markdown (alignment <div>, <br>, ...) is
* stripped: only the Markdown itself is converted to HTML, keeping the body
* clean and safe for the wysiwyg format.
*/
private function getMarkdownConverter(): CommonMarkConverter {
if ($this->markdownConverter === NULL) {
$this->markdownConverter = new CommonMarkConverter([
'html_input' => 'strip',
'allow_unsafe_links' => FALSE,
]);
}
return $this->markdownConverter;
}
}
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
@@ -0,0 +1,93 @@
Copyright 2020 The Epilogue Project Authors (https://github.com/Etcetera-Type-Co/Epilogue)
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
https://openfontlicense.org
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
@@ -0,0 +1,81 @@
Epilogue Variable Font
======================
This download contains Epilogue as both variable fonts and static fonts.
Epilogue is a variable font with this axis:
wght
This means all the styles are contained in these files:
Epilogue-VariableFont_wght.ttf
Epilogue-Italic-VariableFont_wght.ttf
If your app fully supports variable fonts, you can now pick intermediate styles
that arent available as static fonts. Not all apps support variable fonts, and
in those cases you can use the static font files for Epilogue:
static/Epilogue-Thin.ttf
static/Epilogue-ExtraLight.ttf
static/Epilogue-Light.ttf
static/Epilogue-Regular.ttf
static/Epilogue-Medium.ttf
static/Epilogue-SemiBold.ttf
static/Epilogue-Bold.ttf
static/Epilogue-ExtraBold.ttf
static/Epilogue-Black.ttf
static/Epilogue-ThinItalic.ttf
static/Epilogue-ExtraLightItalic.ttf
static/Epilogue-LightItalic.ttf
static/Epilogue-Italic.ttf
static/Epilogue-MediumItalic.ttf
static/Epilogue-SemiBoldItalic.ttf
static/Epilogue-BoldItalic.ttf
static/Epilogue-ExtraBoldItalic.ttf
static/Epilogue-BlackItalic.ttf
Get started
-----------
1. Install the font files you want to use
2. Use your app's font picker to view the font family and all the
available styles
Learn more about variable fonts
-------------------------------
https://developers.google.com/web/fundamentals/design-and-ux/typography/variable-fonts
https://variablefonts.typenetwork.com
https://medium.com/variable-fonts
In desktop apps
https://theblog.adobe.com/can-variable-fonts-illustrator-cc
https://helpx.adobe.com/nz/photoshop/using/fonts.html#variable_fonts
Online
https://developers.google.com/fonts/docs/getting_started
https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Fonts/Variable_Fonts_Guide
https://developer.microsoft.com/en-us/microsoft-edge/testdrive/demos/variable-fonts
Installing fonts
MacOS: https://support.apple.com/en-us/HT201749
Linux: https://www.google.com/search?q=how+to+install+a+font+on+gnu%2Blinux
Windows: https://support.microsoft.com/en-us/help/314960/how-to-install-or-remove-a-font-in-windows
Android Apps
https://developers.google.com/fonts/docs/android
https://developer.android.com/guide/topics/ui/look-and-feel/downloadable-fonts
License
-------
Please read the full license text (OFL.txt) to understand the permissions,
restrictions and requirements for usage, redistribution, and modification.
You can use them in your products & projects print or digital,
commercial or otherwise.
This isn't legal advice, please consider consulting a lawyer and see the full
license for all details.
@@ -0,0 +1,221 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="robots" content="noindex, noarchive">
<meta name="format-detection" content="telephone=no">
<title>Transfonter demo</title>
<link href="stylesheet.css" rel="stylesheet">
<style>
/*
http://meyerweb.com/eric/tools/css/reset/
v2.0 | 20110126
License: none (public domain)
*/
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
display: block;
}
body {
line-height: 1;
}
ol, ul {
list-style: none;
}
blockquote, q {
quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
/* demo styles */
body {
background: #f0f0f0;
color: #000;
}
.page {
background: #fff;
width: 920px;
margin: 0 auto;
padding: 20px 20px 0 20px;
overflow: hidden;
}
.font-container {
overflow-x: auto;
overflow-y: hidden;
margin-bottom: 40px;
line-height: 1.3;
white-space: nowrap;
padding-bottom: 5px;
}
h1 {
position: relative;
background: #444;
font-size: 32px;
color: #fff;
padding: 10px 20px;
margin: 0 -20px 12px -20px;
}
.letters {
font-size: 25px;
margin-bottom: 20px;
}
.s10:before {
content: '10px';
}
.s11:before {
content: '11px';
}
.s12:before {
content: '12px';
}
.s14:before {
content: '14px';
}
.s18:before {
content: '18px';
}
.s24:before {
content: '24px';
}
.s30:before {
content: '30px';
}
.s36:before {
content: '36px';
}
.s48:before {
content: '48px';
}
.s60:before {
content: '60px';
}
.s72:before {
content: '72px';
}
.s10:before, .s11:before, .s12:before, .s14:before,
.s18:before, .s24:before, .s30:before, .s36:before,
.s48:before, .s60:before, .s72:before {
font-family: Arial, sans-serif;
font-size: 10px;
font-weight: normal;
font-style: normal;
color: #999;
padding-right: 6px;
}
pre {
display: block;
padding: 9px;
margin: 0 0 12px;
font-family: Monaco, Menlo, Consolas, "Courier New", monospace;
font-size: 13px;
line-height: 1.428571429;
color: #333;
font-weight: normal;
font-style: normal;
background-color: #f5f5f5;
border: 1px solid #ccc;
overflow-x: auto;
border-radius: 4px;
}
/* responsive */
@media (max-width: 959px) {
.page {
width: auto;
margin: 0;
}
}
</style>
</head>
<body>
<div class="page">
<div class="demo">
<h1 style="font-family: 'Epilogue'; font-weight: normal; font-style: italic;">Epilogue Italic</h1>
<pre title="Usage">.your-style {
font-family: 'Epilogue';
font-weight: normal;
font-style: italic;
}</pre>
<pre title="Preload (optional)">
&lt;link rel=&quot;preload&quot; href=&quot;Epilogue-Italic.woff2&quot; as=&quot;font&quot; type=&quot;font/woff2&quot; crossorigin&gt;</pre>
<div class="font-container" style="font-family: 'Epilogue'; font-weight: normal; font-style: italic;">
<p class="letters">
abcdefghijklmnopqrstuvwxyz<br>
ABCDEFGHIJKLMNOPQRSTUVWXYZ<br>
0123456789.:,;()*!?'@#&lt;&gt;$%&^+-=~
</p>
<p class="s10" style="font-size: 10px;">The quick brown fox jumps over the lazy dog.</p>
<p class="s11" style="font-size: 11px;">The quick brown fox jumps over the lazy dog.</p>
<p class="s12" style="font-size: 12px;">The quick brown fox jumps over the lazy dog.</p>
<p class="s14" style="font-size: 14px;">The quick brown fox jumps over the lazy dog.</p>
<p class="s18" style="font-size: 18px;">The quick brown fox jumps over the lazy dog.</p>
<p class="s24" style="font-size: 24px;">The quick brown fox jumps over the lazy dog.</p>
<p class="s30" style="font-size: 30px;">The quick brown fox jumps over the lazy dog.</p>
<p class="s36" style="font-size: 36px;">The quick brown fox jumps over the lazy dog.</p>
<p class="s48" style="font-size: 48px;">The quick brown fox jumps over the lazy dog.</p>
<p class="s60" style="font-size: 60px;">The quick brown fox jumps over the lazy dog.</p>
<p class="s72" style="font-size: 72px;">The quick brown fox jumps over the lazy dog.</p>
</div>
</div>
<div class="demo">
<h1 style="font-family: 'Epilogue'; font-weight: normal; font-style: normal;">Epilogue Regular</h1>
<pre title="Usage">.your-style {
font-family: 'Epilogue';
font-weight: normal;
font-style: normal;
}</pre>
<pre title="Preload (optional)">
&lt;link rel=&quot;preload&quot; href=&quot;Epilogue-Regular.woff2&quot; as=&quot;font&quot; type=&quot;font/woff2&quot; crossorigin&gt;</pre>
<div class="font-container" style="font-family: 'Epilogue'; font-weight: normal; font-style: normal;">
<p class="letters">
abcdefghijklmnopqrstuvwxyz<br>
ABCDEFGHIJKLMNOPQRSTUVWXYZ<br>
0123456789.:,;()*!?'@#&lt;&gt;$%&^+-=~
</p>
<p class="s10" style="font-size: 10px;">The quick brown fox jumps over the lazy dog.</p>
<p class="s11" style="font-size: 11px;">The quick brown fox jumps over the lazy dog.</p>
<p class="s12" style="font-size: 12px;">The quick brown fox jumps over the lazy dog.</p>
<p class="s14" style="font-size: 14px;">The quick brown fox jumps over the lazy dog.</p>
<p class="s18" style="font-size: 18px;">The quick brown fox jumps over the lazy dog.</p>
<p class="s24" style="font-size: 24px;">The quick brown fox jumps over the lazy dog.</p>
<p class="s30" style="font-size: 30px;">The quick brown fox jumps over the lazy dog.</p>
<p class="s36" style="font-size: 36px;">The quick brown fox jumps over the lazy dog.</p>
<p class="s48" style="font-size: 48px;">The quick brown fox jumps over the lazy dog.</p>
<p class="s60" style="font-size: 60px;">The quick brown fox jumps over the lazy dog.</p>
<p class="s72" style="font-size: 72px;">The quick brown fox jumps over the lazy dog.</p>
</div>
</div>
</div>
</body>
</html>
@@ -0,0 +1,18 @@
@font-face {
font-family: 'Epilogue';
src: url('Epilogue-Italic-VariableFont_wght.ttf') format('woff2');
src: url('Epilogue-Italic-VariableFont_wght.ttf') format('woff2') tech(variations);
font-weight: 100 900;
font-style: italic;
}
@font-face {
font-family: 'Epilogue';
src: url('Epilogue-VariableFont_wght.ttf') format('woff2');
src: url('Epilogue-VariableFont_wght.ttf') format('woff2') tech(variations);
font-weight: 100 900;
font-style: normal;
}
@@ -0,0 +1,32 @@
import axios from 'axios'
// https://github.com/alvar0hurtad0/drupal-vuejs-todo/blob/master/frontend/src/api/axiosInterceptor.js
// console.log('drupalSettings', drupalSettings)
console.log(window.location)
const JSONAPI = axios.create({
baseURL: `${window.location.origin}/jsonapi`,
withCredentials: true,
headers: {
Accept: 'application/vnd.api+json'
// Authorization: 'Basic {token}',
// 'Content-Type': 'application/json'
}
})
JSONAPI.interceptors.response.use(
response => {
return Promise.resolve(response)
},
error => {
const { status } = error.response
console.warn('error in json-axios', status)
// if (status === 403) {
// window.location = '/'
// }
return Promise.reject(error)
}
)
export default JSONAPI
@@ -0,0 +1,45 @@
import axios from 'axios'
// https://github.com/alvar0hurtad0/drupal-vuejs-todo/blob/master/frontend/src/api/axiosInterceptor.js
// console.log('drupalSettings', drupalSettings)
// console.log('window.location.origin', window.location.origin)
// axios.interceptors.response.use(
// response => {
// return Promise.resolve(response)
// },
// error => {
// const { status } = error.response
// console.warn('error in rest-axios', status)
// if (status === 403) {
// window.location = '/'
// }
// return Promise.reject(error)
// }
// )
const REST = axios.create({
baseURL: `${window.location.origin}`,
withCredentials: true,
headers: {
// Authorization: 'Bearer {token}',
'Content-Type': 'application/json'
}
})
REST.interceptors.response.use(
response => {
return Promise.resolve(response)
},
error => {
const { status } = error.response
console.warn('error in rest-axios', status)
// if (status === 403) {
// window.location = '/'
// }
return Promise.reject(error)
}
)
export default REST
+427
View File
@@ -0,0 +1,427 @@
// import { createApp } from 'vue'
import '../scss/main.scss'
import "splitting/dist/splitting.css";
import "splitting/dist/splitting-cells.css";
import Splitting from "splitting";
import { hyphenateSync } from "hyphen/fr";
import { gsap, Power1 } from 'gsap';
import { ScrollTrigger } from 'gsap/ScrollTrigger';
import Lenis from 'lenis';
import 'fslightbox';
gsap.registerPlugin(ScrollTrigger);
// import Content from './vuejs/Content.vue'
// import REST from './api/rest-axios'
// import LocomotiveScroll from 'locomotive-scroll';
// const scroll = new LocomotiveScroll();
// /**
// * @file
// * https://www.drupal.org/docs/drupal-apis/javascript-api/javascript-api-overview
// */
// (function (Drupal) {
// 'use strict';
// Drupal.behaviors.leshed = {
// attach: function (context, settings) {
// console.log('It works!');
// }
// };
// } (Drupal));
(function (Drupal, drupalSettings) {
const LeShedTheme = function () {
const _is_front = drupalSettings.path.isFront
console.log('drupalSettings', drupalSettings)
const scroller = document.querySelector('main[role="main"]');
const gsap_mm = gsap.matchMedia();
console.log('gsap_mm', gsap_mm);
// ___ _ _
// |_ _|_ _ (_) |_
// | || ' \| | _|
// |___|_||_|_|\__|
function init () {
console.log('LeShedTheme init()');
initTitles();
initBurgerMenu();
initSmoothScroll();
initColumnScrollSync();
initFsLightbox();
// initVues()
}
function onWindowResize() {
_cardsForms.forEach(({card, forme, formepos}) => {
// console.log(`card`, card, 'forme', forme, 'formepos', formepos);
forme.style.top = `${card.offsetTop + formepos.top}px`;
forme.style.left = `${card.offsetLeft + formepos.left}px`;
})
}
function randomInt(min, max) {
return Math.floor(Math.random() * (max - min)) + min;
}
// Rendu typographique des titres. Police Epilogue = axe variable `wght`
// (100900) uniquement ; l'italique est un fichier séparé (font-style).
const TITLE_STYLE = {
// 3 groupes de graisse : thin = majorité (défaut), medium = quelques,
// bold = les pics. Les fines ne sont pas comptées (c'est le reste).
wghtThin: { min: 100, max: 200 }, // majorité (défaut)
wghtMedium: { min: 200, max: 400 }, // quelques
wghtBold: { min: 800, max: 900 }, // pics
boldRatio: 0.28, // proportion de pics gras par mot
mediumRatio: 0.12, // proportion de medium par mot
italicOnBold: 0.6, // proba d'italique sur un pic
italicOnMedium: 0.4, // proba d'italique sur un medium
italicOnThin: 0.30, // proba d'italique sur une fine
};
function initTitles() {
const titles = document.querySelectorAll('#block-leshed-identitedusite>a, article.node-type-projet>header>h2, article.node-type-projet>.cols>.left>h2');
for (const txt of titles) {
txt.classList.add("variable-title");
const splitted = Splitting({ target: txt, by: 'chars' });
for (const word of splitted[0].words) {
const chars = Array.from(word.getElementsByClassName('char'));
styleWordChars(chars);
hyphenateWord(chars);
}
}
const pageTitle = document.querySelector('article.node-type-projet.view-mode-full h2.node-title');
console.log('pageTitle', pageTitle);
if (pageTitle) {
// needs requestAnimationFrame to get the real titleHeight after splitting
requestAnimationFrame(() => {
requestAnimationFrame(() => {
const initTitleH = pageTitle.offsetHeight;
// console.log(`initTitleH: ${initTitleH}`);
const titleScaleTarget = 0.4;
const end = initTitleH - initTitleH * titleScaleTarget;
// console.log(`end: ${end}`);
gsap_mm.add('(min-width: 768px)', () => {
gsap.to(pageTitle, {
scale: titleScaleTarget,
y: end,
ease: 'none',
scrollTrigger: {
scroller: scroller,
start: 0,
end: end, // scroll maximum du conteneur (robuste au resize)
scrub: true,
invalidateOnRefresh: true,
},
});
})
});
});
}
}
/**
* Tire k positions réparties régulièrement dans [0, n[ : le mot est découpé
* en k cases contiguës, une position au hasard par case (aléa contrôlé, pas
* de paquets). Les positions déjà prises par un autre groupe sont évitées.
*/
function spreadPositions(n, k, exclude = new Set()) {
const set = new Set();
for (let i = 0; i < k; i++) {
const start = Math.floor((i * n) / k);
const end = Math.floor(((i + 1) * n) / k); // borne exclue
const span = Math.max(1, end - start);
let pos = randomInt(start, start + span);
let guard = 0;
while ((exclude.has(pos) || set.has(pos)) && guard < span) {
pos = start + ((pos - start + 1) % span);
guard++;
}
set.add(pos);
}
return set;
}
/**
* Applique graisse + italique caractère par caractère.
*
* Majorité de fines ; quelques medium et quelques pics gras, chacun réparti
* régulièrement dans le mot. L'italique est corrélé à la graisse.
*/
function styleWordChars(chars) {
const n = chars.length;
if (!n) return;
// Accents répartis régulièrement : d'abord les pics gras, puis les medium
// (en évitant les positions déjà grasses). Le reste sera fin.
const bold = spreadPositions(n, Math.round(n * TITLE_STYLE.boldRatio));
const medium = spreadPositions(n, Math.round(n * TITLE_STYLE.mediumRatio), bold);
// 1) Décide graisse + italique pour chaque caractère.
const decisions = chars.map((char, i) => {
let range, pItal;
if (bold.has(i)) {
range = TITLE_STYLE.wghtBold;
pItal = TITLE_STYLE.italicOnBold;
}
else if (medium.has(i)) {
range = TITLE_STYLE.wghtMedium;
pItal = TITLE_STYLE.italicOnMedium;
}
else {
range = TITLE_STYLE.wghtThin;
pItal = TITLE_STYLE.italicOnThin;
}
return {
char,
wght: randomInt(range.min, range.max + 1),
ital: Math.random() < pItal,
};
});
// 2) Au moins une italique par mot : si aucune, on en force une, de
// préférence sur un pic gras ou un medium, sinon sur une lettre au hasard.
if (!decisions.some((d) => d.ital)) {
const accents = [...bold, ...medium];
const idx = accents.length ? accents[randomInt(0, accents.length)] : randomInt(0, n);
decisions[idx].ital = true;
}
// 3) Pas plus de 2 italiques consécutives : on casse les séries de 3+.
let run = 0;
for (const d of decisions) {
if (d.ital) {
run += 1;
if (run > 2) {
d.ital = false;
run = 0;
}
}
else {
run = 0;
}
}
// 4) Applique.
for (const { char, wght, ital } of decisions) {
char.style.fontVariationSettings = `'wght' ${wght}`;
char.style.fontStyle = ital ? 'italic' : 'normal';
char.classList.toggle('italic', ital);
char.classList.toggle('normal', !ital);
}
}
/**
* Insère des césures logicielles (U+00AD) aux points de coupure valides.
*
* Comme chaque lettre est un span (display:inline) sans « run » de texte
* continu, `hyphens: auto` est inopérant : on calcule donc les points de
* césure (motifs FR) et on insère un soft hyphen — en nœud texte, entre les
* lettres concernées — qui n'autorise une coupure (avec tiret) qu'à ces
* endroits. Les mots trop longs coupent proprement au lieu de casser
* n'importe où.
*/
function hyphenateWord(chars) {
if (chars.length < 5) return;
const SOFT = '\u00AD';
const text = chars.map((c) => c.textContent).join('');
const hyphenated = hyphenateSync(text);
if (!hyphenated.includes(SOFT)) return;
let letter = 0;
for (const ch of hyphenated) {
if (ch === SOFT) {
const target = chars[letter];
if (letter > 0 && target && target.parentNode) {
target.parentNode.insertBefore(document.createTextNode(SOFT), target);
}
}
else {
letter++;
}
}
}
function initBurgerMenu() {
let header_right = document.getElementById('burger-btn');
header_right.parentElement.addEventListener('click', function(e){
// console.log('click header_right', this);
this.firstElementChild.toggleAttribute('opened');
this.firstElementChild.nextElementSibling.toggleAttribute('opened');
})
}
/**
* Scroll lissé (Lenis) sur le conteneur `main` — et non la fenêtre, car
* c'est `main[role="main"]` qui porte l'overflow. Lenis est branché sur le
* ticker GSAP (une seule boucle RAF) et pousse ses updates à ScrollTrigger
* pour que la synchro des colonnes reste calée sur le scroll lissé.
*/
function initSmoothScroll() {
const scroller = document.querySelector('main[role="main"]');
const content = scroller && scroller.querySelector('.layout-content');
if (!scroller || !content) return;
const lenis = new Lenis({
wrapper: scroller, // conteneur scrollable custom
content: content, // contenu réel (le 1er enfant est l'ancre skip-link)
duration: 1.2,
smoothWheel: true,
});
lenis.on('scroll', ScrollTrigger.update);
gsap.ticker.add((time) => lenis.raf(time * 1000));
gsap.ticker.lagSmoothing(0);
}
/**
* Synchronise le scroll des deux colonnes du layout 50/50 : la colonne la
* plus courte est ralentie (translateY décroissant) pour que son bas soit
* atteint en même temps que celui de la colonne la plus haute, qui défile
* nativement (flux normal, pas de pin). Le scroll réel a lieu dans
* `main[role="main"]`, pas dans la fenêtre : GSAP utilise donc ce conteneur
* comme `scroller`.
*/
function initColumnScrollSync() {
console.log('initColumnScrollSync');
const firstColumn = document.querySelector('.layout--twocol-section--50-50 .layout__region--first');
const secondColumn = document.querySelector('.layout--twocol-section--50-50 .layout__region--second');
console.log(scroller, firstColumn, secondColumn);
if (!scroller || !firstColumn || !secondColumn) return;
gsap_mm.add('(min-width: 768px)', () => {
[firstColumn, secondColumn].forEach((column) => {
gsap.to(column, {
// Distance à parcourir pour que le bas de la colonne arrive en
// bas du viewport en même temps que la colonne la plus haute.
y: () => {
const visibleHeight = scroller.clientHeight;
const scrollRange = scroller.scrollHeight - scroller.clientHeight;
const columnRevealRange = Math.max(0, column.offsetHeight - visibleHeight);
return scrollRange - columnRevealRange;
},
ease: 'none',
scrollTrigger: {
scroller: scroller,
start: 0,
end: 'max', // scroll maximum du conteneur (robuste au resize)
// Valeur numérique = easing : l'animation met ~0.6s à rattraper la
// position de scroll au lieu de la suivre au pixel près → lisse les
// à-coups de la colonne lente. Monter la valeur = plus d'inertie.
scrub: true,
invalidateOnRefresh: true,
},
});
});
// Nettoyage automatique des tweens/triggers quand on repasse sous 768px.
return () => {};
});
// Les images chargent après coup et changent la hauteur des colonnes.
window.addEventListener('load', () => ScrollTrigger.refresh());
}
function initFsLightbox(){
console.log('initFsLightbox');
// console.log(window.FsLightbox);
let imgs_links = document.querySelectorAll('article.node-type-projet.view-mode-full .field_images.multiple .field-item a');
for (const link of imgs_links) {
link.setAttribute("data-fslightbox", "gallery")
}
// window.FsLightbox
window.refreshFsLightbox();
}
// function initVues(){
// console.log('initVues');
// // initVueContent();
// }
// function initVueContent(){
// createApp(Content).mount('main[role="main"]');
// // processEtapeLinks();
// }
// function onClickEtapeLink(e){
// e.preventDefault();
// let a = e.currentTarget;
// let nid = a.dataset.nodeNid;
// console.log(nid);
// getNodeData(nid);
// return null;
// }
// function processEtapeLinks(){
// let etape_link_fields = document.querySelectorAll('#etapes-liste div.views-field-title');
// etape_link_fields.forEach((field, index) => {
// let nid = null;
// let classList = field.classList;
// classList.forEach((classe) => {
// let reg = /data-node-(\d+)/;
// let result = classe.match(reg);
// if (result) {
// nid = result[1];
// console.log(nid);
// }
// })
// if (nid) {
// let a = field.querySelector('a');
// a.setAttribute('data-node-nid', nid);
// a.addEventListener('click', onClickEtapeLink);
// }
// })
// }
// function getNodeData(nid){
// const params = {
// }
// REST.get(`/node/${nid}?_format=json`, params)
// .then((data) => {
// console.log('user REST getUser data', data)
// })
// .catch(error => {
// console.warn('Issue with getNodedata', error)
// Promise.reject(error)
// })
// }
init()
} // end LeShedTheme()
LeShedTheme()
})(Drupal, drupalSettings)
@@ -0,0 +1,13 @@
<template>
<div>
hello HMR TROP TROP BIEN
</div>
</template>
<script>
</script>
<style>
</style>
@@ -0,0 +1,391 @@
@import "partials/variables";
@import "partials/colors";
@import "partials/fonts";
@import "partials/mixins";
a, a:visited{
text-decoration: none;
color: inherit;
transition: color 0.15s ease-out;
}
body{
margin: 0;
// background-color: green;
font-weight: $default_fwght;
line-height: 1.5;
}
// Splitting met .word ET .char en inline-block :
// - inline-block sur .char crée une opportunité de coupure entre CHAQUE lettre ;
// - inline-block sur .word (dimensionné en max-content) empêche tout retour à la
// ligne interne, donc les soft hyphens ne peuvent jamais couper le mot.
// On repasse les deux en inline : le mot redevient un flux de texte qui ne coupe
// qu'aux césures logicielles (U+00AD) insérées par le JS.
// Spécificité (0,2,1) pour battre `.splitting .word/.char` de Splitting.
.splitting span.word,
.splitting span.char{
display: inline;
}
// _ _
// | | | |
// | | __ _ _ _ ___ _ _| |_
// | | / _` | | | |/ _ \| | | | __|
// | |___| (_| | |_| | (_) | |_| | |_
// \_____/\__,_|\__, |\___/ \__,_|\__|
// __/ |
// |___/
div.layout-container{
display: flex;
flex-direction: column;
height: 100vh;
overflow: hidden;
max-width:1080px;
margin: 0 auto;
header[role="banner"]{
display: block;
position: relative;
padding: 0.5em 0;
flex: 1 1 $header_height;
z-index: 100;
// position: fixed;
// top:0;
// width: 100%;
height: $header_height;
min-height: $header_height;
}
main[role="main"]{
flex: 1 1 auto;
// padding:5em 0;
overflow-y: scroll;
}
// footer[role="contentinfo"]{
// // flex: 0 1 content;
// z-index: 100;
// position: fixed;
// bottom: 0;
// width: 100%;
// }
}
// _ _ _
// | | | | | |
// | |_| | ___ __ _ __| | ___ _ __
// | _ |/ _ \/ _` |/ _` |/ _ \ '__|
// | | | | __/ (_| | (_| | __/ |
// \_| |_/\___|\__,_|\__,_|\___|_|
header[role="banner"]{
background-color: #fff;
// box-shadow: 0 -5px 15px #000;
.header-middle{
display: flex;
justify-content:space-between;
align-items: center;
padding: 0.2em 0;
}
#block-leshed-identitedusite{
a{
// color: ;
text-decoration: none;
font-size: 2em;
font-weight: 500;
margin-right: 0.5em;
// &:hover{
// color: $rose;
// }
}
}
.header-right{
// position: relative;
height:20px; width:20px;
cursor: pointer;
// background-color: green;
>div#burger-btn{
position:relative;
transition-duration: 0.5s;
transition-property: transform;
top: 8.5px;
&, &::before, &::after{
// position: absolute;
width:20px; height:3px;
background-color: #000;
}
&:hover{
&, &::before, &::after{
background-color: green;
}
}
&::before, &::after{
content: "";
position: absolute;
left: 0;
// background-color: red;
}
&::before{
top:6px;
}
&::after{
bottom: 6px;
}
&[opened]{
transform: rotate(90deg);
}
}
&>div.wrapper{
position:absolute;
top:$header_height; left:0;
// z-index: 99;
background-color: rgba(0,0,0,0.75);
width:100vw;
height: 0;
overflow: hidden;
transition-duration: 0.5s;
&[opened]{
height: calc(100vh - $header_height);
}
&>nav{
padding-top: 10px;
background-color: #fff;
>ul{
margin: 0;
}
}
nav#block-mathallo-navigationprincipale{
padding: 2em 1em;
ul{
display: flex;
justify-content: flex-end;
list-style: none;
li{
margin-right: 1em;
// background-color: $bleu_site;
color: #fff;
padding:0.5em 1em;
&:not(:last-child){
}
}
}
}
}
}
} // end of header[role="banner"]{
.variable-title{
margin: 0;
font-weight: 200;
text-transform:lowercase;
line-height: 1;
// margin-left: -0.04em;
filter: drop-shadow(0 0 5px #fff) drop-shadow(0 0 15px #fff);
span.word{
span.char{
letter-spacing:-0.1em;
}
}
// span.whitespace{
// letter-spacing: 0.5em;
// }
}
// _____ _____ __ ____ ___ _ ____
// |_ _/ _ \ \ / / / ___/ _ \| | / ___|
// | || | | \ \ /\ / / | | | | | | | \___ \
// | || |_| |\ V V / | |__| |_| | |___ ___) |
// |_| \___/ \_/\_/ \____\___/|_____|____/
div.layout.layout--twocol-section--50-50{
flex-wrap: nowrap;
// Sans ça, `align-items: stretch` (défaut flex) étire les deux colonnes à la
// même hauteur : la synchro de scroll ne verrait aucune différence de hauteur.
// flex-start = chaque colonne prend la hauteur de son contenu.
align-items: flex-start;
column-gap: $colsgap;
div.layout__region{
max-width: calc(50% - $colsgap / 2);
flex: 1 0 calc(50% - $colsgap / 2);
.views-row{
// border: 1px solid red;
// box-sizing: border-box;
margin-bottom: 2em;
max-width: 100%;
article.node-type-projet{
>header{
>h2{
font-size: 5em;
}
}
>div{
// margin-top: 3em;
}
// only with images, title overlap the image
&.has-image{
>header{
>h2{
position: relative;
max-height: 1em;
overflow: visible;
z-index: 10;
// text-shadow: 0 0 10px #fff;
// box-shadow: 0 0 15px #f00;
// filter: drop-shadow(0 0 5px #fff);
}
div.field_images{
position:relative;
// width: 100%;
z-index: 5;
a{
display:block;
img{
width:100%;
height: auto;
}
}
}
}
}
}
}
}
}
// ____ _ _
// | _ \ _ __ ___ (_) ___| |_
// | |_) | '__/ _ \| |/ _ \ __|
// | __/| | | (_) | | __/ |_
// |_| |_| \___// |\___|\__|
// |__/
article.node-type-projet.view-mode-full{
div.cols{
display: flex;
flex-direction: row;
gap: $colsgap;
// column-gap: 2em;
div.col{
flex:0 0 calc(50% - $colsgap / 2);
max-width: calc(50% - $colsgap / 2);
&.left{
>h2.node-title{
position: relative;
font-size: 12em;
margin: 0;
z-index: 100;
width: 205%;//calc(50% - $colsgap / 2);
pointer-events: none;
transform-origin: 0 0;
}
.field_artiste_s_invite_s{
font-size: 1.8em;
font-weight: 300;
}
.field_invite_accompagne_curate_p{
font-weight: 400;
.field-label{
font-weight: $default_fwght;
}
>div{
display: inline-block;
}
}
.field_dates{
time{
font-weight: 400;
}
}
}
&.right{
// Étire .field_images sur toute la hauteur de la colonne (= colonne la
// plus haute, via le stretch de .cols) : le contexte de collage sticky
// couvre alors tout le scroll, donc les cartes restent figées tant que
// la colonne gauche défile encore (cf. point 6).
display: flex;
.field_images{
flex: 1;
// .field-items{
&:after{
display: block;
position: relative;
content: "";
// border: 1px solid red;
width: 100%;
height: 100vh;
}
// }
.field-item{
position: relative;
// Empilement "cartes" : chaque image se fige au même endroit (haut) et
// est recouverte par la suivante (l'ordre du DOM fait que la dernière
// passe au-dessus). La 1re image ne bouge pas. Desktop uniquement.
@media (min-width: 768px){
position: sticky;
top: 0;
// &:last-child:after{
// content: "";
// // position: relative;
// display:block;
// // blockquote.image-field-caption{
// height: 100vh;
// width:100%;
// border: 1px solid red;
// // }
// }
}
img{
width: 100%;
max-width: 100%;
height: auto;
}
blockquote.image-field-caption{
margin: 0;
position: absolute;
bottom: 0; left: 0;
width: 100%;
background-color: rgba(0,0,0,0.65);
color: #fff;
p{
margin: 0;
padding: 1em;
font-weight: 300;
font-size: 0.8em;
}
opacity: 0;
transition: opacity 0.3s ease-in-out;
}
&:hover{
z-index:200;
blockquote.image-field-caption{
opacity: 1;
}
}
}
}
}
}
}
}
@@ -0,0 +1,3 @@
// $bleu_site: #1642bc;
// $bleu_typo: #0b1e52;
// $rose: #f661e2;
@@ -0,0 +1,31 @@
@import "/assets/fonts/epilogue/stylesheet.css";
body{
font-family: 'Epilogue';
font-weight: normal;
font-style: normal;
}
@mixin titre{
}
// @mixin titre_h2{
// font-family: 'Fredoka';
// font-size: 3em;
// font-weight: 400;
// color: $bleu_typo;
// line-height: 1;
// }
// @mixin titre_h3{
// @include titre_h2();
// font-size: 2em;
// }
// @mixin titre_h4{
// @include titre_h2();
// font-size: 1.323em;
// }
@@ -0,0 +1,69 @@
@mixin menu-contenus{
ul,li{
padding: 0;
margin:0;
list-style: none;
}
.wrapper{
position: relative;
}
label{
color:$bleu_site;
font-weight: 600;
display: block;
font-size: 0.756em;
padding:0 0 0.25em 0;
transition: color 0.25s ease-out;
position: absolute;
z-index: 0;
&:has(+ a:hover){
color: $rose;
}
}
a{
font-size: 1.13em;
transition: color 0.15s ease-out;
display: block;
position: relative;
z-index: 1;
padding-top: 1em;
&:hover{
color: $bleu_site;
}
}
}
@mixin card{
position: relative;
background-color: #fff;
padding:2em;
box-shadow: 0 0 10px rgba(0,0,0,0.25);
max-width: 50em;
box-sizing: content-box;
margin: 30vh auto 0;
&[role="home-presentation"]{
margin-top: 10vh;
}
}
@mixin arrow{
display: block;
width:30px; height:30px;
border-radius: 15px;
background-repeat: no-repeat;
background-position: center center;
background-size: contain;
text-indent: 100px;
overflow: hidden;
}
@mixin arrow-white{
@include arrow();
background-image: url('/themes/custom/mathallo/assets/img/arrow-white.svg');
}
@mixin arrow-blue{
@include arrow();
background-image: url('/themes/custom/mathallo/assets/img/arrow-blue.svg');
}
@@ -0,0 +1,3 @@
$default_fwght: 200;
$header_height: 70px;
$colsgap: 2em;
+25
View File
@@ -0,0 +1,25 @@
name: Le Shed
type: theme
base theme: stark
description: leshed drupal theme based on stark
package: leshed
core: 0.x
core_version_requirement: ^8 || ^9 || ^10 || ^11
# libraries:
# - caravane/global
regions:
header_top: 'Header-top'
header_left: 'Header-left'
header_center: 'Header-center'
header_right: 'Header-right'
header_bottom: 'Header-bottom'
primary_menu: 'Primary menu'
secondary_menu: 'Secondary menu'
page_top: 'Page top'
page_bottom: 'Page bottom'
featured: 'Featured'
breadcrumb: 'Breadcrumb'
content: 'Content'
sidebar_first: 'Sidebar first'
sidebar_second: 'Sidebar second'
footer: 'Footer'
@@ -0,0 +1,20 @@
# Main theme library.
global:
js:
assets/dist/main.js: {}
# assets/js/main.js: {}
css:
theme:
assets/dist/main.css: {}
# assets/scss/main.css: {}
vitehmr:
js:
http://localhost:5173/@vite/client:
type: external
attributes:
type: module
http://localhost:5173/assets/js/main.js:
type: external
attributes:
type: module
+114
View File
@@ -0,0 +1,114 @@
<?php
/**
* @file
* Functions to support theming in the leshed theme.
*/
function leshed_page_attachments_alter(&$page) {
if (getenv('APP_ENV') === 'development') {
// $attachments['#attached']['html_head'][] = [['src' => 'http://localhost:5173/@vite/client', 'type' => 'module'],'vite-client'];
// $page['#attached']['library'][] = 'http://localhost:5173/@vite/client';
// $attachments['#attached']['html_head'][] = [['src' => 'http://localhost:5173/assets/js/main.js', 'type' => 'module'],'vite-main'];
// $page['#attached']['library'][] = 'http://localhost:5173/assets/js/main.js';
$page['#attached']['library'][] = 'leshed/vitehmr';
}else{
$page['#attached']['library'][] = 'leshed/global';
}
}
/**
* Implements hook_preprocess_HOOK() for html.html.twig.
*/
function leshed_preprocess_html(&$variables) {
}
/**
* Implements hook_preprocess_HOOK() for page.html.twig.
*/
function leshed_preprocess_page(&$variables) {
}
function leshed_preprocess_region(&$variables) {
if (!isset($variables["attributes"]['class'])) {
$variables["attributes"]['class'] = [];
}
$variables["attributes"]['class'][] = "wrapper";
}
// function leshed_preprocess_block__leshed_contenu(&$variables) {
function leshed_preprocess_block__menu_block__contenu(&$variables) {
foreach ($variables['content']['#items'] as $key => $item) {
parse_menu_item($variables['content']['#items'], $key);
}
}
function parse_menu_item(&$items, $key){
if ($items[$key]['url']->getRouteName() === "entity.node.canonical") {
$nid = $items[$key]['url']->getRouteParameters()['node'];
$node = \Drupal\node\Entity\Node::load($nid);
// create prefix (label) for chapitres
if ($node->getType() === "chapitre") {
$chapitre = $node->get('field_chapitre_num')->getValue()[0]['value'];
$items[$key]['prefix'] = "Chapitre {$chapitre}";
}
// create prefix (label) for parties
if ($node->getType() === "partie") {
$partie = $node->get('field_partie')->getValue()[0]['value'];
$items[$key]['prefix'] = "Partie {$partie}";
}
// add active attribute
if ($items[$key]['in_active_trail']){
$items[$key]['attributes']->offsetSet('class', 'in-active-trail');
}
}
if (count($items[$key]['below']) > 0) {
foreach ($items[$key]['below'] as $key_b => $item_b) {
parse_menu_item($items[$key]['below'], $key_b);
}
}
}
/**
* Implements hook_preprocess_HOOK() for node.html.twig.
*/
function leshed_preprocess_node(&$variables) {
/** @var \Drupal\node\Entity\Node $node */
$node = $variables['node'];
$node_type = $node->getType();
if (!isset($variables["attributes"]['class'])) {
$variables["attributes"]['class'] = [];
}
$variables["attributes"]['class'][] = "node-type-{$node_type}";
$variables["attributes"]['class'][] = "view-mode-{$variables['view_mode']}";
if ($variables['view_mode'] === "teaser") {
/** @var \Drupal\file\Plugin\\Field\FieldType\FileFieldItemList $field_images */
$field_images = $node->get('field_images');
if (!$field_images->isEmpty()) {
$variables["attributes"]['class'][] = "has-image";
}
}
}
function leshed_preprocess_container(&$variables) {
}
/**
* Implements hook_preprocess_HOOK() for node.html.twig.
*/
function leshed_preprocess_field(&$variables) {
if (!isset($variables["title_attributes"]['class'])) {
$variables["title_attributes"]['class'] = [];
}
$variables['title_attributes']['class'][] = 'field-label';
if (!isset($variables["attributes"]['class'])) {
$variables["attributes"]['class'] = [];
}
$variables['attributes']['class'][] = $variables['field_name'];
}
File diff suppressed because it is too large Load Diff
+25
View File
@@ -0,0 +1,25 @@
{
"name": "leshed",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"axios": "^1.7.2",
"fslightbox": "^3.7.5",
"gsap": "^3.14.1",
"hyphen": "^1.14.1",
"lenis": "^1.3.26",
"splitting": "^1.1.0",
"vue": "^3.4.29"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.0.5",
"sass": "^1.77.8",
"vite": "^5.3.1"
}
}
+6
View File
@@ -0,0 +1,6 @@
Sass
```
npm install
npm run scss
```
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

@@ -0,0 +1,72 @@
{#
/**
* @file
* Default theme implementation for a field.
*
* To override output, copy the "field.html.twig" from the templates directory
* to your theme's directory and customize it, just like customizing other
* Drupal templates such as page.html.twig or node.html.twig.
*
* Instead of overriding the theming for all fields, you can also just override
* theming for a subset of fields using
* @link themeable Theme hook suggestions. @endlink For example,
* here are some theme hook suggestions that can be used for a field_foo field
* on an article node type:
* - field--node--field-foo--article.html.twig
* - field--node--field-foo.html.twig
* - field--node--article.html.twig
* - field--field-foo.html.twig
* - field--text-with-summary.html.twig
* - field.html.twig
*
* Available variables:
* - attributes: HTML attributes for the containing element.
* - label_hidden: Whether to show the field label or not.
* - title_attributes: HTML attributes for the title.
* - label: The label for the field.
* - multiple: TRUE if a field can contain multiple items.
* - items: List of all the field items. Each item contains:
* - attributes: List of HTML attributes for each item.
* - content: The field item's content.
* - entity_type: The entity type to which the field belongs.
* - field_name: The name of the field.
* - field_type: The type of the field.
* - label_display: The display settings for the label.
*
* @see template_preprocess_field()
*
* @ingroup themeable
*/
#}
{%
set title_classes = [
label_display == 'visually_hidden' ? 'visually-hidden',
]
%}
{% if label_hidden %}
{% if multiple %}
<div{{ attributes.addClass("field-items").addClass("multiple") }}>
{% for item in items %}
<div{{ item.attributes.addClass("field-item") }}>{{ item.content }}</div>
{% endfor %}
</div>
{% else %}
{% for item in items %}
<div{{ attributes.addClass("field-item") }}>{{ item.content }}</div>
{% endfor %}
{% endif %}
{% else %}
<div{{ attributes }}>
<div{{ title_attributes.addClass(title_classes) }}>{{ label }}</div>
{% if multiple %}
<div class="field-items multiple">
{% endif %}
{% for item in items %}
<div{{ item.attributes.addClass("field-item") }}>{{ item.content }}</div>
{% endfor %}
{% if multiple %}
</div>
{% endif %}
</div>
{% endif %}
@@ -0,0 +1,49 @@
{#
/**
* @file
* Default theme implementation for the basic structure of a single Drupal page.
*
* Variables:
* - logged_in: A flag indicating if user is logged in.
* - root_path: The root path of the current page (e.g., node, admin, user).
* - node_type: The content type for the current node, if the page is a node.
* - head_title: List of text elements that make up the head_title variable.
* May contain one or more of the following:
* - title: The title of the page.
* - name: The name of the site.
* - slogan: The slogan of the site.
* - page_top: Initial rendered markup. This should be printed before 'page'.
* - page: The rendered page markup.
* - page_bottom: Closing rendered markup. This variable should be printed after
* 'page'.
* - db_offline: A flag indicating if the database is offline.
* - placeholder_token: The token for generating head, css, js and js-bottom
* placeholders.
*
* @see template_preprocess_html()
*
* @ingroup themeable
*/
#}
<!DOCTYPE html>
<html{{ html_attributes }}>
<head>
<head-placeholder token="{{ placeholder_token }}">
<title>{{ head_title|safe_join(' | ') }}</title>
<css-placeholder token="{{ placeholder_token }}">
<js-placeholder token="{{ placeholder_token }}">
</head>
<body{{ attributes }}>
{#
Keyboard navigation/accessibility link to main content section in
page.html.twig.
#}
<a href="#main-content" class="visually-hidden focusable">
{{ 'Skip to main content'|t }}
</a>
{{ page_top }}
{{ page }}
{{ page_bottom }}
<js-bottom-placeholder token="{{ placeholder_token }}">
</body>
</html>
@@ -0,0 +1,89 @@
{#
/**
* @file
* Default theme implementation to display a node.
*
* Available variables:
* - node: The node entity with limited access to object properties and methods.
* Only method names starting with "get", "has", or "is" and a few common
* methods such as "id", "label", and "bundle" are available. For example:
* - node.getCreatedTime() will return the node creation timestamp.
* - node.hasField('field_example') returns TRUE if the node bundle includes
* field_example. (This does not indicate the presence of a value in this
* field.)
* - node.isPublished() will return whether the node is published or not.
* Calling other methods, such as node.delete(), will result in an exception.
* See \Drupal\node\Entity\Node for a full list of public properties and
* methods for the node object.
* - label: (optional) The title of the node.
* - content: All node items. Use {{ content }} to print them all,
* or print a subset such as {{ content.field_example }}. Use
* {{ content|without('field_example') }} to temporarily suppress the printing
* of a given child element.
* - author_picture: The node author user entity, rendered using the "compact"
* view mode.
* - metadata: Metadata for this node.
* - date: (optional) Themed creation date field.
* - author_name: (optional) Themed author name field.
* - url: Direct URL of the current node.
* - display_submitted: Whether submission information should be displayed.
* - attributes: HTML attributes for the containing element.
* The attributes.class element may contain one or more of the following
* classes:
* - node: The current template type (also known as a "theming hook").
* - node--type-[type]: The current node type. For example, if the node is an
* "Article" it would result in "node--type-article". Note that the machine
* name will often be in a short form of the human readable label.
* - node--view-mode-[view_mode]: The View Mode of the node; for example, a
* teaser would result in: "node--view-mode-teaser", and
* full: "node--view-mode-full".
* The following are controlled through the node publishing options.
* - node--promoted: Appears on nodes promoted to the front page.
* - node--sticky: Appears on nodes ordered above other non-sticky nodes in
* teaser listings.
* - node--unpublished: Appears on unpublished nodes visible only to site
* admins.
* - title_attributes: Same as attributes, except applied to the main title
* tag that appears in the template.
* - content_attributes: Same as attributes, except applied to the main
* content tag that appears in the template.
* - author_attributes: Same as attributes, except applied to the author of
* the node tag that appears in the template.
* - title_prefix: Additional output populated by modules, intended to be
* displayed in front of the main title tag that appears in the template.
* - title_suffix: Additional output populated by modules, intended to be
* displayed after the main title tag that appears in the template.
* - view_mode: View mode; for example, "teaser" or "full".
* - teaser: Flag for the teaser state. Will be true if view_mode is 'teaser'.
* - page: Flag for the full page state. Will be true if view_mode is 'full'.
*
* @see template_preprocess_node()
*
* @ingroup themeable
*/
#}
<article{{ attributes }}>
{{ title_prefix }}
{% if label and not page %}
<h2{{ title_attributes }}>
<a href="{{ url }}" rel="bookmark">{{ label }}</a>
</h2>
{% endif %}
{{ title_suffix }}
{% if display_submitted %}
<footer>
{{ author_picture }}
<div{{ author_attributes }}>
{% trans %}Submitted by {{ author_name }} on {{ date }}{% endtrans %}
{{ metadata }}
</div>
</footer>
{% endif %}
<div{{ content_attributes }}>
{{ content }}
</div>
</article>
@@ -0,0 +1,79 @@
{#
/**
* @file
* Default theme implementation to display a node.
*
* Available variables:
* - node: The node entity with limited access to object properties and methods.
* Only method names starting with "get", "has", or "is" and a few common
* methods such as "id", "label", and "bundle" are available. For example:
* - node.getCreatedTime() will return the node creation timestamp.
* - node.hasField('field_example') returns TRUE if the node bundle includes
* field_example. (This does not indicate the presence of a value in this
* field.)
* - node.isPublished() will return whether the node is published or not.
* Calling other methods, such as node.delete(), will result in an exception.
* See \Drupal\node\Entity\Node for a full list of public properties and
* methods for the node object.
* - label: (optional) The title of the node.
* - content: All node items. Use {{ content }} to print them all,
* or print a subset such as {{ content.field_example }}. Use
* {{ content|without('field_example') }} to temporarily suppress the printing
* of a given child element.
* - author_picture: The node author user entity, rendered using the "compact"
* view mode.
* - metadata: Metadata for this node.
* - date: (optional) Themed creation date field.
* - author_name: (optional) Themed author name field.
* - url: Direct URL of the current node.
* - display_submitted: Whether submission information should be displayed.
* - attributes: HTML attributes for the containing element.
* The attributes.class element may contain one or more of the following
* classes:
* - node: The current template type (also known as a "theming hook").
* - node--type-[type]: The current node type. For example, if the node is an
* "Article" it would result in "node--type-article". Note that the machine
* name will often be in a short form of the human readable label.
* - node--view-mode-[view_mode]: The View Mode of the node; for example, a
* teaser would result in: "node--view-mode-teaser", and
* full: "node--view-mode-full".
* The following are controlled through the node publishing options.
* - node--promoted: Appears on nodes promoted to the front page.
* - node--sticky: Appears on nodes ordered above other non-sticky nodes in
* teaser listings.
* - node--unpublished: Appears on unpublished nodes visible only to site
* admins.
* - title_attributes: Same as attributes, except applied to the main title
* tag that appears in the template.
* - content_attributes: Same as attributes, except applied to the main
* content tag that appears in the template.
* - author_attributes: Same as attributes, except applied to the author of
* the node tag that appears in the template.
* - title_prefix: Additional output populated by modules, intended to be
* displayed in front of the main title tag that appears in the template.
* - title_suffix: Additional output populated by modules, intended to be
* displayed after the main title tag that appears in the template.
* - view_mode: View mode; for example, "teaser" or "full".
* - teaser: Flag for the teaser state. Will be true if view_mode is 'teaser'.
* - page: Flag for the full page state. Will be true if view_mode is 'full'.
*
* @see template_preprocess_node()
*
* @ingroup themeable
*/
#}
<article{{ attributes }}>
<div class="cols">
<div class="col left">
<h2{{ title_attributes.addClass('node-title') }}>
{{ label }}
</h2>
{{ content|without('field_images') }}
</div>
<div class="col right">
{{ content.field_images }}
</div>
</div>
</article>

Some files were not shown because too many files have changed in this diff Show More