diff --git a/assets/styles/app.css b/assets/styles/app.css index 3438eeac1..8f9b0d9fd 100644 --- a/assets/styles/app.css +++ b/assets/styles/app.css @@ -1,7 +1,12 @@ +/* Vendors */ @import "../../vendor/tempest/highlight/src/Themes/Css/catppuccin-latte.css"; + +/* Tailwind */ +@import "tailwindcss"; + +/* AFUP */ @import "./fonts.css"; -@import "tailwindcss" source(none); -@source "../../templates"; +@import "./prose.css"; @plugin "@tailwindcss/typography"; @@ -26,12 +31,6 @@ --color-erreur-500: #C10825; /* Texte d'un message d'erreur */ --color-erreur-700: #960018; - - /* Surcharge de la police par défaut des pages */ - --font-sans: "Inter", ui-sans-serif, system-ui, sans-serif; - - /* Police utilisée pour les titres */ - --font-titre: "Glegoo", ui-serif, Georgia, serif; } @layer base { @@ -39,10 +38,4 @@ /* Désactive les effets (italic, gras, etc) automatiques des polices pour prioriser les version contenues dans les polices */ font-synthesis: none; } - - /* Pour les balises de code "inline" dans les articles (les blocs de code ont déjà un padding et un arrondi de base) */ - .prose code { - @apply px-2 py-1; - @apply rounded-lg; - } } diff --git a/assets/styles/fonts.css b/assets/styles/fonts.css index 75537a2fb..f54683c7b 100644 --- a/assets/styles/fonts.css +++ b/assets/styles/fonts.css @@ -21,3 +21,11 @@ font-display: swap; src: url("../fonts/glegoo-latin-700-normal.woff2") format("woff2"); } + +@theme { + /* Surcharge de la police par défaut des pages */ + --font-sans: "Inter", ui-sans-serif, system-ui, sans-serif; + + /* Police utilisée pour les titres */ + --font-titre: "Glegoo", ui-serif, Georgia, serif; +} diff --git a/assets/styles/prose.css b/assets/styles/prose.css new file mode 100644 index 000000000..78253de92 --- /dev/null +++ b/assets/styles/prose.css @@ -0,0 +1,21 @@ +/* + Ce fichier contient des surcharges pour le HTML généré à partir de markdown + Tailwind fournit une lib pour styliser ce genre de HTML : https://github.com/tailwindlabs/tailwindcss-typography +*/ + +@layer base { + /* Pour les balises de code "inline" dans les articles (les blocs de code ont déjà un padding et un arrondi de base) */ + .prose code { + @apply px-2 py-1; + @apply rounded-lg; + } + + /* Gestion de certaines pages CMS avec des boutons */ + /* Les classes sont les mêmes que pour le composant Button */ + /* @see templates/components/Button.html.twig */ + .prose a.button.button--call-to-action { + @apply items-center! justify-center! rounded-lg! border! border-transparent! bg-clip-border! text-sm! font-medium! whitespace-nowrap! px-3! py-2!; + @apply bg-ruby-500! text-white! hover:bg-ruby-700!; + @apply no-underline!; + } +} diff --git a/config/services.yaml b/config/services.yaml index 6adb25fe0..ace4eb2a5 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -148,12 +148,10 @@ services: class: Parsedown autowire: true - Twig\Extra\Markdown\MarkdownInterface: '@twig.markdown.default' + twig.markdown.default: + alias: AppBundle\Markdown\Markdown - League\CommonMark\Extension\Autolink\AutolinkExtension: - tags: [ 'twig.markdown.league_extension' ] - Tempest\Highlight\CommonMark\HighlightExtension: - tags: [ 'twig.markdown.league_extension' ] + Twig\Extra\Markdown\MarkdownInterface: '@twig.markdown.default' geocoder: class: Geocoder\StatefulGeocoder diff --git a/sources/AppBundle/Markdown/Markdown.php b/sources/AppBundle/Markdown/Markdown.php new file mode 100644 index 000000000..ba78dd9b5 --- /dev/null +++ b/sources/AppBundle/Markdown/Markdown.php @@ -0,0 +1,44 @@ + [ + 'symbol' => '', + 'apply_id_to_heading' => true, + 'insert' => HeadingPermalinkProcessor::INSERT_NONE, + ], + 'slug_normalizer' => [ + 'instance' => new SymfonySlugNormalizer(), + ], + ]); + $environment->addExtension(new CommonMarkCoreExtension()); + $environment->addExtension(new AutolinkExtension()); + $environment->addExtension(new HeadingPermalinkExtension()); + $environment->addExtension(new HighlightExtension()); + + $this->converter = new MarkdownConverter($environment); + } + + public function convert(string $body): string + { + return $this->converter->convert($body)->getContent(); + } +} diff --git a/sources/AppBundle/Markdown/SymfonySlugNormalizer.php b/sources/AppBundle/Markdown/SymfonySlugNormalizer.php new file mode 100644 index 000000000..f86ea7050 --- /dev/null +++ b/sources/AppBundle/Markdown/SymfonySlugNormalizer.php @@ -0,0 +1,16 @@ +slug($text)->lower()->toString(); + } +} diff --git a/sources/AppBundle/Twig/Components/MainMenu.php b/sources/AppBundle/Twig/Components/MainMenu.php index 69e431118..6af0bf742 100644 --- a/sources/AppBundle/Twig/Components/MainMenu.php +++ b/sources/AppBundle/Twig/Components/MainMenu.php @@ -8,25 +8,29 @@ use AppBundle\Security\Authentication; use AppBundle\Site\Entity\Feuille as FeuilleEntity; use AppBundle\Site\Entity\Repository\FeuilleRepository; +use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\Security\Core\User\UserInterface; use Symfony\UX\TwigComponent\Attribute\AsTwigComponent; #[AsTwigComponent] -final readonly class MainMenu +final class MainMenu { + public ?int $currentFeuilleId = null; + public function __construct( - private RequestStack $requestStack, - private FeuilleRepository $feuilleRepository, - private Authentication $authentication, + private readonly RequestStack $requestStack, + private readonly FeuilleRepository $feuilleRepository, + private readonly Authentication $authentication, ) {} /** - * @return array + * @return array{main: array, sub: array} */ - public function getFeuilles(): array + public function getEntries(): array { $feuillesEnfants = $this->feuilleRepository->getFeuillesEnfant(Feuille::ID_FEUILLE_HEADER); + $request = $this->requestStack->getMainRequest(); if ($this->authentication->getAfupUserOrNull() instanceof UserInterface) { $feuilleLogin = new FeuilleEntity(); @@ -42,7 +46,7 @@ public function getFeuilles(): array $feuillesEnfants[] = $feuilleLogin; } - $currentUri = $this->requestStack->getCurrentRequest()?->getRequestUri() ?? ''; + $currentUri = $request?->getRequestUri() ?? ''; $feuilles = []; foreach ($feuillesEnfants as $feuille) { @@ -85,11 +89,51 @@ public function getFeuilles(): array $feuilles[] = [ 'isCurrent' => $isCurrent, - 'lien' => $feuille->lien, - 'nom' => $feuille->nom, + 'feuille' => $feuille, ]; } - return $feuilles; + $subEntries = []; + if ($this->currentFeuilleId !== null && $request !== null) { + $menu = $this->feuilleRepository->getFeuillesEnfant($this->currentFeuilleId); + + foreach ($menu as $feuille) { + $subEntries[] = [ + 'feuille' => $feuille, + 'is_active' => $this->isSubEntryActive($request, $feuille), + ]; + } + } + + return [ + 'main' => $feuilles, + 'sub' => $subEntries, + ]; + } + + private function isSubEntryActive(Request $request, FeuilleEntity $feuille): bool + { + $url = $request->getUri(); + + $pattern = '/' . preg_quote((string) $feuille->lien, '/') . '/'; + + if (preg_match($pattern, $url)) { + return true; + } + + if ($feuille->patterns) { + foreach (explode(PHP_EOL, $feuille->patterns) as $pattern) { + $pattern = trim($pattern); + if ($pattern === '') { + continue; + } + + if (preg_match($pattern, $url)) { + return true; + } + } + } + + return false; } } diff --git a/templates/components/MainMenu.html.twig b/templates/components/MainMenu.html.twig index a1c7c3cd6..ddb05a371 100644 --- a/templates/components/MainMenu.html.twig +++ b/templates/components/MainMenu.html.twig @@ -1,11 +1,22 @@ diff --git a/templates/components/MainMenu/Item.html.twig b/templates/components/MainMenu/Item.html.twig index 86fa45181..00bdbd82c 100644 --- a/templates/components/MainMenu/Item.html.twig +++ b/templates/components/MainMenu/Item.html.twig @@ -2,7 +2,7 @@ {# @block content Label du menu #} {%- props variant = 'default' -%} {%- set style = html_cva( - base: "px-4 py-4 font-inter whitespace-nowrap", + base: "p-4 font-inter whitespace-nowrap", variants: { variant: { default: 'hover:text-black hover:bg-white', diff --git a/templates/layouts/site.html.twig b/templates/layouts/site.html.twig index 6c6e7b135..6c34192f3 100644 --- a/templates/layouts/site.html.twig +++ b/templates/layouts/site.html.twig @@ -40,7 +40,9 @@ - + {% set menu_current_feuille_id = menu_current_feuille_id|default(null) %} + + {{ account_buttons }} diff --git a/templates/site/cms_page/display.html.twig b/templates/site/cms_page/display.html.twig index 03046db09..869fddbbd 100644 --- a/templates/site/cms_page/display.html.twig +++ b/templates/site/cms_page/display.html.twig @@ -1,23 +1,14 @@ -{% extends 'site/base.html.twig' %} +{% extends 'layouts/site.html.twig' %} +{% set menu_current_feuille_id = rubrique.feuilleAssociee %} {% block title %}{{ article.titre }} - AFUP{% endblock %} -{% block header %} - {{ parent() }} - -{% endblock %} - {% block content %} -{{ render(controller( - 'AppBundle\\Controller\\Website\\SecondaryMenuController::display', - { 'feuille_id': rubrique.feuilleAssociee } -)) }} -
-
-

{{ rubrique.nom }}

-

{{ article.titre }}

+
+

{{ article.titre }}

+
{{ article.contenu|markdown_to_html }}
-
+ {% endblock %} diff --git a/tests/behat/features/PublicSite/News.feature b/tests/behat/features/PublicSite/News.feature index 0ce6c05fe..238a0b24b 100644 --- a/tests/behat/features/PublicSite/News.feature +++ b/tests/behat/features/PublicSite/News.feature @@ -32,7 +32,10 @@ Feature: Site Public - News When I follow "Actualités" And I follow "Lire l'article: Un article en Markdown" Then I should not see "### Un premier titre !" - And the response should contain the html "

Un premier titre !

" + And the response should contain the html + """ +

Un premier titre !

+ """ And I should not see "**de texte en gras**" And the response should contain the html "de texte en gras" And I should not see "*de texte en italic*" diff --git a/tests/behat/features/PublicSite/Rss.feature b/tests/behat/features/PublicSite/Rss.feature index a95d2c45f..137728524 100644 --- a/tests/behat/features/PublicSite/Rss.feature +++ b/tests/behat/features/PublicSite/Rss.feature @@ -9,6 +9,9 @@ Feature: Site Public - Flux RSS And the response should contain "" And the response should contain "<![CDATA[Les vidéos des talks du Forum PHP 2018 sont disponibles]]>" And the response should contain "https://apachephptest:80/news/1-les-videos-du-forum-2018-en-ligne" - And the response should contain "

Un premier titre !

" + And the response should contain the html + """ +

Un premier titre !

+ """ And the response should contain "Et un peu de texte en italic" And the response should contain "
"