diff --git a/assets/vue/components/layout/Sidebar.vue b/assets/vue/components/layout/Sidebar.vue index 6820edf38ff..879ad6be66b 100644 --- a/assets/vue/components/layout/Sidebar.vue +++ b/assets/vue/components/layout/Sidebar.vue @@ -40,6 +40,7 @@
+

@@ -82,6 +83,7 @@ import PageList from "../page/PageList.vue" import { useEnrolledStore } from "../../store/enrolledStore" import BaseIcon from "../basecomponents/BaseIcon.vue" import BaseSidebarPanelMenu from "../basecomponents/BaseSidebarPanelMenu.vue" +import CategoryLinks from "../page/CategoryLinks.vue" const { t } = useI18n() const securityStore = useSecurityStore() diff --git a/assets/vue/components/page/CategoryLinks.vue b/assets/vue/components/page/CategoryLinks.vue new file mode 100644 index 00000000000..7ba5d0638d0 --- /dev/null +++ b/assets/vue/components/page/CategoryLinks.vue @@ -0,0 +1,44 @@ + + + diff --git a/src/CoreBundle/Controller/PageController.php b/src/CoreBundle/Controller/PageController.php index 1d706876056..1cb652b99a0 100644 --- a/src/CoreBundle/Controller/PageController.php +++ b/src/CoreBundle/Controller/PageController.php @@ -103,4 +103,25 @@ public function preview(int $id, PageRepository $pageRepo): Response 'page' => $page, ]); } + + /** + * Public endpoint used by Vue (sidebar + login page) to render legal menu links. + * + * GET /pages/_category-links?category=menu_links&locale=fr_FR + */ + #[Route('/_category-links', name: 'public_page_category_links', methods: ['GET'])] + public function categoryLinks(Request $request, PageRepository $pageRepo): JsonResponse + { + $accessUrl = $this->accessUrlHelper->getCurrent(); + $locale = trim((string) $request->query->get('locale', '')); + $category = trim((string) $request->query->get('category', '')); + + if ('' === $category) { + return $this->json(['items' => []]); + } + + $items = $pageRepo->findPublicLinksByCategoryWithLocaleFallback($accessUrl, $category, $locale); + + return $this->json(['items' => $items]); + } } diff --git a/src/CoreBundle/Helpers/PageHelper.php b/src/CoreBundle/Helpers/PageHelper.php index b803e1afabd..ec40edef3fe 100644 --- a/src/CoreBundle/Helpers/PageHelper.php +++ b/src/CoreBundle/Helpers/PageHelper.php @@ -64,19 +64,19 @@ public function createDefaultPages(User $user, AccessUrl $url, string $locale): ; $this->pageCategoryRepository->update($indexCategory); - $indexCategory = (new PageCategory()) + $faqCategory = (new PageCategory()) ->setTitle('faq') ->setType('grid') ->setCreator($user) ; - $this->pageCategoryRepository->update($indexCategory); + $this->pageCategoryRepository->update($faqCategory); - $indexCategory = (new PageCategory()) + $demoCategory = (new PageCategory()) ->setTitle('demo') ->setType('grid') ->setCreator($user) ; - $this->pageCategoryRepository->update($indexCategory); + $this->pageCategoryRepository->update($demoCategory); $page = (new Page()) ->setTitle('Welcome') @@ -87,7 +87,6 @@ public function createDefaultPages(User $user, AccessUrl $url, string $locale): ->setEnabled(true) ->setUrl($url) ; - $this->pageRepository->update($page); $indexPage = (new Page()) @@ -106,7 +105,6 @@ public function createDefaultPages(User $user, AccessUrl $url, string $locale): ->setType('grid') ->setCreator($user) ; - $this->pageCategoryRepository->update($footerPublicCategory); $footerPrivateCategory = (new PageCategory()) @@ -114,9 +112,15 @@ public function createDefaultPages(User $user, AccessUrl $url, string $locale): ->setType('grid') ->setCreator($user) ; - $this->pageCategoryRepository->update($footerPrivateCategory); + $menuLinksCategory = (new PageCategory()) + ->setTitle('menu_links') + ->setType('grid') + ->setCreator($user) + ; + $this->pageCategoryRepository->update($menuLinksCategory); + // Categories for extra content in admin blocks. foreach (PageCategory::ADMIN_BLOCKS_CATEGORIES as $nameBlock) { $usersAdminBlock = (new PageCategory()) @@ -132,7 +136,6 @@ public function createDefaultPages(User $user, AccessUrl $url, string $locale): ->setType('grid') ->setCreator($user) ; - $this->pageCategoryRepository->update($publicCategory); $introductionCategory = (new PageCategory()) diff --git a/src/CoreBundle/Migrations/Schema/V200/Version20260204142200.php b/src/CoreBundle/Migrations/Schema/V200/Version20260204142200.php new file mode 100644 index 00000000000..df237dd9ee2 --- /dev/null +++ b/src/CoreBundle/Migrations/Schema/V200/Version20260204142200.php @@ -0,0 +1,50 @@ +entityManager->getRepository(PageCategory::class); + $adminUser = $this->getAdmin(); + + // Create the "menu_links" category if it does not exist yet. + $existing = $pageCategoryRepo->findOneBy(['title' => 'menu_links']); + if ($existing) { + error_log('[MIGRATION] PageCategory "menu_links" already exists. Skipping.'); + + return; + } + + $category = new PageCategory(); + $category + ->setTitle('menu_links') + ->setType('grid') + ->setCreator($adminUser) + ; + + $this->entityManager->persist($category); + $this->entityManager->flush(); + + error_log('[MIGRATION] PageCategory "menu_links" created successfully.'); + } + + public function down(Schema $schema): void + { + // Intentionally left empty (non-destructive migration). + } +} diff --git a/src/CoreBundle/Repository/PageRepository.php b/src/CoreBundle/Repository/PageRepository.php index 2f451e685c4..8b8744a1638 100644 --- a/src/CoreBundle/Repository/PageRepository.php +++ b/src/CoreBundle/Repository/PageRepository.php @@ -102,4 +102,71 @@ public function delete(?Page $page = null): void $this->getEntityManager()->flush(); } } + + public function findPublicLinksByCategoryWithLocaleFallback( + AccessUrl $accessUrl, + string $categoryTitle, + string $locale + ): array { + $locale = trim($locale); + $prefix = '' !== $locale ? substr($locale, 0, 2) : ''; + + // Exact locale + if ('' !== $locale) { + $items = $this->createQueryBuilder('p') + ->select('p.title AS title, p.slug AS slug') + ->innerJoin('p.category', 'c') + ->andWhere('p.enabled = true') + ->andWhere('p.url = :url') + ->andWhere('c.title = :cat') + ->andWhere('p.locale = :loc') + ->setParameter('url', $accessUrl) + ->setParameter('cat', $categoryTitle) + ->setParameter('loc', $locale) + ->orderBy('p.title', 'ASC') + ->getQuery() + ->getArrayResult() + ; + + if (!empty($items)) { + return $items; + } + } + + // Prefix locale (e.g. "fr") + if ('' !== $prefix) { + $items = $this->createQueryBuilder('p') + ->select('p.title AS title, p.slug AS slug') + ->innerJoin('p.category', 'c') + ->andWhere('p.enabled = true') + ->andWhere('p.url = :url') + ->andWhere('c.title = :cat') + ->andWhere('p.locale LIKE :prefix') + ->setParameter('url', $accessUrl) + ->setParameter('cat', $categoryTitle) + ->setParameter('prefix', $prefix.'%') + ->orderBy('p.title', 'ASC') + ->getQuery() + ->getArrayResult() + ; + + if (!empty($items)) { + return $items; + } + } + + // Any locale + return $this->createQueryBuilder('p') + ->select('p.title AS title, p.slug AS slug') + ->innerJoin('p.category', 'c') + ->andWhere('p.enabled = true') + ->andWhere('p.url = :url') + ->andWhere('c.title = :cat') + ->setParameter('url', $accessUrl) + ->setParameter('cat', $categoryTitle) + ->orderBy('p.title', 'ASC') + ->getQuery() + ->getArrayResult() + ; + } } diff --git a/src/CoreBundle/Resources/views/Layout/base-layout.html.twig b/src/CoreBundle/Resources/views/Layout/base-layout.html.twig index 408d2fc9006..238e2368a02 100644 --- a/src/CoreBundle/Resources/views/Layout/base-layout.html.twig +++ b/src/CoreBundle/Resources/views/Layout/base-layout.html.twig @@ -26,6 +26,9 @@ {{ modals_block }} +{# Cookie banner (global) #} +{% include "@ChamiloCore/Layout/cookie_banner.html.twig" %} + {% include "@ChamiloCore/Layout/foot.html.twig" %} diff --git a/src/CoreBundle/Resources/views/Layout/cookie_banner.html.twig b/src/CoreBundle/Resources/views/Layout/cookie_banner.html.twig new file mode 100644 index 00000000000..061c2a4ce2b --- /dev/null +++ b/src/CoreBundle/Resources/views/Layout/cookie_banner.html.twig @@ -0,0 +1,101 @@ +{# Cookie banner (C1-compatible behavior) #} +{% set cookieWarningRaw = chamilo_settings_get('platform.cookie_warning')|default('')|trim|lower %} +{% set cookieWarningEnabled = (cookieWarningRaw == 'true') %} +{% set cookieAck = app.request.cookies.get('ChamiloUsesCookies') %} +{% set youAcceptCookiesText = + 'By continuing to use this site, you declare you accept its use of cookies.'|trans +%} +{% set helpCookieUsageValidationHtml = + "In order for this site to work and be able to measure the use of content, this platform uses cookies.

\nIf needed, the Help section of your browser indicates how to configure cookies.

\nFor more information on cookies, you can visit the About Cookies website."|trans +%} +{% set noCookiesText = + 'You do not have cookies support enabled in your browser. Chamilo relies on the feature called "cookies" to store your connection details. This means you will not be able to login if cookies are not enabled. Please change your browser configuration (generally in the Edit -> Preferences menu) and reload this page.'|trans +%} +{% if cookieWarningEnabled and cookieAck != 'ok' %} +

+ + +{% endif %}