templates/base.html.twig line 1

Open in your IDE?
  1. <!DOCTYPE html>
  2. <html lang="{{ app.request.locale }}">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <title>GENCAT | {% block title %}{{ 'header.title'|trans({}, 'base') }}{% endblock %}</title>
  7. {% set route = app.request.attributes.get('_route') %}
  8. {% set parts = route|split('_') %}
  9. {% set paths = [] %}
  10. {% for i in 1..parts|length %}
  11. {% set paths = paths|merge([parts|slice(0, i)|join('_')]) %}
  12. {% endfor %}
  13. <!-- Hoja de estilos general para el layout -->
  14. {% block stylesheets %}
  15. <link rel="stylesheet" href="{{ asset("css/style.css") }}">
  16. <link rel="stylesheet" href="{{ asset("css/layout.css") }}">
  17. <link rel="stylesheet" href="{{ asset('css/modal.css') }}">
  18. <link rel="stylesheet" href="{{ asset('css/modal-bootstrap-dialog.css') }}">
  19. <link rel="stylesheet" href="{{ asset('css/chatbot.css') }}">
  20. <link rel="stylesheet" href="{{ asset('css/tables.css') }}">
  21. <link rel="stylesheet" href="{{ asset('css/sort.css') }}">
  22. <!-- FontAwesome CSS -->
  23. <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css">
  24. <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap3-dialog/1.34.7/css/bootstrap-dialog.min.css">
  25. <link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
  26. <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.13/css/select2.min.css">
  27. {# <link href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap-glyphicons.css" rel="stylesheet">#}
  28. {# añadimos según el perfil de usuario su css correspondiente #}
  29. {% if app.session.get('rol') %}
  30. {% set perfil = app.session.get('rol') | lower %}
  31. {% if css_exists('css/' ~ perfil ~ '/style.css') %}
  32. <link rel="stylesheet" href="{{ asset('css/' ~ perfil ~ '/style.css') }}">
  33. {% endif %}
  34. {% if css_exists('css/' ~ perfil ~ '/layout.css') %}
  35. <link rel="stylesheet" href="{{ asset('css/' ~ perfil ~ '/layout.css') }}">
  36. {% endif %}
  37. {% endif %}
  38. {% for path in paths %}
  39. {% if css_exists('css/' ~ path ~ '/style.css') %}
  40. <link rel="stylesheet" href="{{ asset('css/' ~ path ~ '/style.css') }}">
  41. {% endif %}
  42. {% if css_exists('css/' ~ path ~ '/layout.css') %}
  43. <link rel="stylesheet" href="{{ asset('css/' ~ path ~ '/layout.css') }}">
  44. {% endif %}
  45. {% endfor %}
  46. {% endblock %}
  47. <!-- JavaScript -->
  48. {% block javascripts %}
  49. <script src="https://code.jquery.com/jquery-3.7.1.min.js" integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=" crossorigin="anonymous"></script>
  50. <script src="https://cdn.jsdelivr.net/npm/jquery-validation@1.19.3/dist/jquery.validate.js"
  51. crossorigin="anonymous"></script>
  52. <script src="https://cdn.jsdelivr.net/npm/jquery-validation@1.19.3/dist/localization/messages_es.js"
  53. crossorigin="anonymous"></script>
  54. <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.27.0/moment.min.js" type="text/javascript"></script>
  55. <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.27.0/locale/es.min.js"
  56. type="text/javascript"></script>
  57. <script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.13/js/select2.min.js"
  58. type="text/javascript"></script>
  59. <script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.13/js/i18n/es.js"
  60. type="text/javascript"></script>
  61. <script src="https://cdnjs.cloudflare.com/ajax/libs/jcarousel/0.3.9/jquery.jcarousel.min.js" integrity="sha512-5TU8T3STShZiLsdqDqiKnj0Z72ccPZpIDCuItxc2S7G3lyiwqiuLuDFVNsLQ7Hgu5f33DlZ2KAJspbn6NAXqnA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
  62. {# Define missing asset variable that app.js expects #}
  63. <script>
  64. // Fix for missing asset variable that breaks existing JavaScriptf
  65. // TODO lo comento porque está dando muchos problemas con app.js del código existente
  66. {#if (typeof asset === 'undefined') {#}
  67. {# window.asset = '{{ app.request.getBasePath() }}/';#}
  68. {# console.log('🔧 Asset variable defined as:', window.asset);#}
  69. {#}#}
  70. // Fix for missing datetimepicker that breaks existing JavaScript
  71. $(document).ready(function() {
  72. // Add a dummy datetimepicker function if it doesn't exist
  73. if (typeof $.fn.datetimepicker === 'undefined') {
  74. $.fn.datetimepicker = function() {
  75. console.log('🔧 Dummy datetimepicker called for:', this);
  76. return this; // Return the jQuery object for chaining
  77. };
  78. }
  79. });
  80. </script>
  81. <script src="{{ asset('js/app.js') }}" type="text/javascript"></script>
  82. <script src="{{ asset('js/functionality.js') }}" type="text/javascript"></script>
  83. <script src="{{ asset('js/taborder.js') }}" type="text/javascript"></script>
  84. {% include 'JS/translations_js.html.twig' %}
  85. {% include 'JS/env_js.html.twig' %}
  86. {% include 'JS/routes_js.html.twig' %}
  87. {# <script src="{{ asset("assets/bootstrap-dialog/js/bootstrap-dialog.min.js") }}"></script>#}
  88. <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
  89. <script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap3-dialog/1.34.7/js/bootstrap-dialog.min.js"></script>
  90. <script src="{{ asset("js/alert_utils.js") }}"></script>
  91. {% include 'notification.js.twig' %}
  92. <script type="text/javascript">
  93. $(document).ready(function() {
  94. $("select").select2();
  95. // Handle alert close button
  96. $(document).on('click', '.close-alert', function() {
  97. $(this).closest('.alert').fadeOut(300, function() {
  98. $(this).remove();
  99. });
  100. });
  101. });
  102. /**
  103. * Clear all form filters and reload page
  104. */
  105. function clearFilters() {
  106. {% block clearFilters %}
  107. $("section#filter input:not([type='hidden']), section#filter select").each(function(key, value) {
  108. if($(value).attr("type") === "checkbox" || $(value).attr("type") === "radio")
  109. {
  110. $(value).prop("checked", false);
  111. }
  112. else
  113. {
  114. $(value).val(null).trigger("change");
  115. }
  116. });
  117. $("section#filter form").submit();
  118. {% endblock %}
  119. }
  120. </script>
  121. {% endblock %}
  122. <link rel="stylesheet" href="{{ asset('css/responsive.css') }}">
  123. </head>
  124. <body id="{{ route }}" class="{{ app.session.get('rol')|lower|default('default') }}" data-env="{{ app.environment }}">
  125. {% block header_logo_hidden %}
  126. <h1 class="h1-hidden">{{ 'header.logo'|trans({}, 'base') }}</h1>
  127. {% endblock %}
  128. <!-- Header -->
  129. <header>
  130. {% block header %}
  131. <section id="header-top-bar">
  132. <div class="header-top-container">
  133. {% block header_top_bar %}
  134. <div class="header-logo">
  135. {% block header_logo %}
  136. <div class="logo">
  137. <a href="https://web.gencat.cat/ca/inici">
  138. <img src="{{ asset("logo/gencat-w.svg") }}" alt="Logo">
  139. </a>
  140. </div>
  141. {% endblock %}
  142. </div>
  143. {# {% block header_2_wrapper %}#}
  144. {# <div class="header-2">#}
  145. {# {% block header_title_secondary %}#}
  146. {# {{ 'header.title.secondary'|trans({}, 'base') }}#}
  147. {# {% endblock %}#}
  148. {# </div>#}
  149. {# {% endblock %}#}
  150. {% block menu_wrapper %}
  151. <ul class="menu">
  152. {% block header_menu %}
  153. <li><a href="{{ path('content_html', { 'slug': 'contact' }) }}">{{ 'header.menu.contact'|trans({}, 'base') }}</a></li>
  154. <span class="separator">|</span>
  155. <li>
  156. {% block select_language %}
  157. {# <a href="#">{{ 'header.menu.language'|trans({}, 'base') }}</a> #}
  158. <select id="select-language" onchange="redirectToPage()">
  159. {% set localesArray = locales|split(',') %}
  160. {% for key, localeLiteral in localesLiteral|split(',') %}
  161. <option value="{{ localesArray[key] | trim }}"
  162. {% if (localesArray[key] | trim) == app.request.locale %}selected{% endif %}
  163. data-url="{{ path(app.request.attributes.get('_route'), app.request.attributes.get('_route_params') | merge({ '_locale': (localesArray[key] | trim) })) }}">{{ localeLiteral }}</option>
  164. {% endfor %}
  165. </select>
  166. <script>
  167. function redirectToPage() {
  168. let select = document.getElementById("select-language");
  169. let url = select.options[select.selectedIndex].getAttribute("data-url");
  170. if (url) {
  171. window.location.href = url;
  172. }
  173. }
  174. </script>
  175. {% endblock select_language %}
  176. </li>
  177. {% endblock %}
  178. </ul>
  179. {% endblock %}
  180. {% block header_3_wrapper %}
  181. {% block header_private_space %}
  182. {% if app.user is empty %}
  183. <div class="header-3">
  184. <a href="{{ path('login_request_credentials') }}">{{ 'header.private_space'|trans({}, 'base') }}</a>
  185. </div>
  186. {% endif %}
  187. {% endblock %}
  188. {% endblock %}
  189. {% endblock %}
  190. </div>
  191. </section>
  192. {% block header_top_bar_2 %}
  193. <section id="header-top-bar-2">
  194. <div class="header-top-container">
  195. {% block header_2_wrapper %}
  196. <div class="header-2">
  197. {% block header_title_secondary %}
  198. <a href="{{ path('home') }}">{{ 'header.title.secondary'|trans({}, 'base') }}</a>
  199. {% endblock %}
  200. </div>
  201. {% endblock %}
  202. </div>
  203. </section>
  204. {% endblock %}
  205. {% block header_links_wrapper %}
  206. <section id="header-links">
  207. <div class="header-top-container">
  208. <div id="header-links-menu">
  209. {% block header_links_menu %}
  210. {% if app.user %}
  211. <div id="menu-app">
  212. {% set rol = app.session.get('rol') | default(constant('App\\Enum\\RolEnum::PARTICIPANTE')) %}
  213. {{ menu('APP_' ~ rol, app.request.locale, '', 'nav-menu-app-'~ rol | lower , 'app_user_type_menu.html.twig') }}
  214. </div>
  215. {% else %}
  216. <a href="{{ path('course_list') }}">{{ 'header.link.search_courses'|trans({}, 'home') }}</a>
  217. <span class="separator">|</span>
  218. <a href="{{ path('orientation_point_list') }}">{{ 'header.link.orientation_points'|trans({}, 'home') }}</a>
  219. <span class="separator">|</span>
  220. <a href="{{ path('login_request_credentials') }}">{{ 'header.link.your_space'|trans({}, 'home') }}</a>
  221. {% endif %}
  222. {% endblock %}
  223. </div>
  224. <div id="header-links-info">
  225. {% block header_links_info %}
  226. {% if app.user %}
  227. {% set rol = app.session.get('rol') | default(constant('App\\Enum\\RolEnum::PARTICIPANTE')) %}
  228. <div id="user-data">
  229. {{ app.user.getPropietarioContenidoByRol(rol).nombreCompleto ?? app.user.nombreCompleto }}
  230. <div id="menu-user">
  231. {{ menu('USER_' ~ rol, app.request.locale, '', 'nav-menu-user-'~ rol | lower, 'user_type_menu.html.twig') }}
  232. </div>
  233. </div>
  234. {% endif %}
  235. {% endblock %}
  236. </div>
  237. </div>
  238. </section>
  239. {% endblock %}
  240. {% block wrapper_header_miga_pan %}
  241. <section id="miga-pan">
  242. {% if app.session.get('rol')%}
  243. {% set pathHome = path('home'~"_"~ app.session.get('rol')|lower) %}
  244. {% else %}
  245. {% set pathHome = path('home') %}
  246. {% endif %}
  247. {% block header_miga_pan %}
  248. <a href="{{ pathHome }}">{{ 'header.breadcrumbs.home'|trans({}, 'base') }}</a>
  249. {% endblock %}
  250. </section>
  251. {% endblock %}
  252. {% block wrapper_header_title %}
  253. <section id="title">
  254. {% block header_title %}
  255. {% endblock %}
  256. </section>
  257. {% endblock %}
  258. {% endblock %}
  259. </header>
  260. <!-- Main Content -->
  261. <main>
  262. {% if 'HomeController' in app.request.get('_controller') and app.user and app.session.get('rol') != constant('App\\Enum\\RolEnum::CONSORCI')%}
  263. {% set propietarioContenido = app.user.getPropietarioContenidoByRol(app.session.get('rol')) %}
  264. {% set alertFillProfile = propietarioContenido ? propietarioContenido.alertFillProfile : null %}
  265. {% if propietarioContenido and (alertFillProfile or alertFillProfile is null) %}
  266. <div class="alert alert-danger">
  267. <i class="fas fa-exclamation-circle alert-icon"></i>
  268. {% set urlProfile = path((app.session.get('rol') ~ '_profile') | lower) %}
  269. {{ 'alert.fill.profile' | trans({ '%url%': urlProfile }) | raw }}
  270. <span class="close-alert"><i class="fas fa-times"></i></span>
  271. </div>
  272. {% endif %}
  273. {% endif %}
  274. {% for tipo, mensajes in app.flashes %}
  275. {% set icon = 'fa-info-circle' %}
  276. {% if tipo == 'success' %}
  277. {% set icon = 'fa-check-circle' %}
  278. {% elseif tipo == 'error' or tipo == 'danger' %}
  279. {% set icon = 'fa-exclamation-circle' %}
  280. {% elseif tipo == 'warning' %}
  281. {% set icon = 'fa-exclamation-triangle' %}
  282. {% endif %}
  283. {% for mensaje in mensajes %}
  284. <div class="alert alert-{{ tipo }}">
  285. <i class="fas {{ icon }} alert-icon"></i>
  286. {{ mensaje }}
  287. <span class="close-alert"><i class="fas fa-times"></i></span>
  288. </div>
  289. {% endfor %}
  290. {% endfor %}
  291. {% block content %}
  292. {% endblock %}
  293. </main>
  294. <!-- Prefooter -->
  295. {% block prefooter_wrapper %}
  296. {% endblock %}
  297. <!-- Footer -->
  298. <footer id="footer">
  299. {% block footer %}
  300. <div class="logo">
  301. <a href="https://www.gencat.cat/" target="_blank"><img src="{{ asset("logo/logo_generalitat_color_b.png") }}"></a>
  302. <a href="https://european-union.europa.eu/" target="_blank"><img src="{{ asset("logo/eu_cofi_feder_color_1.png") }}"></a>
  303. </div>
  304. <div class="footer-links">
  305. <a href="{{ path('content_html', { 'slug': 'legal-notice' }) }}">{{ 'footer.legal_notice'|trans({}, 'base') }}</a>
  306. <span class="separator">|</span>
  307. <a href="{{ path('content_html', { 'slug': 'accessibility' }) }}">{{ 'footer.accessibility'|trans({}, 'base') }}</a>
  308. <span class="separator">|</span>
  309. <a href="{{ path('content_html', { 'slug': 'cookies-policy' }) }}">{{ 'footer.cookies_policy'|trans({}, 'base') }}</a>
  310. <span class="separator">|</span>
  311. <a href="{{ path('content_html', { 'slug': 'sitemap' }) }}">{{ 'footer.sitemap'|trans({}, 'base') }}</a>
  312. </div>
  313. {% endblock %}
  314. {% block chatbot %}
  315. {# Include the chatbot component #}
  316. {% include 'functionality/chatbot/chatbot.html.twig' %}
  317. {% endblock %}
  318. </footer>
  319. <!-- Loading -->
  320. {% block loading %}
  321. <div id="loading">
  322. <img src="{{ asset('img/loadon.gif') }}" width="100px" alt="{{ 'loading.text'|trans({}, 'base') }}">
  323. </div>
  324. {% endblock %}
  325. {% block modal %}
  326. {% include 'modal/modal_change_rol.html.twig' %}
  327. {% include 'modal/modal_admin_user.html.twig' %}
  328. {% endblock %}
  329. {# TinyMCE CDN para editor de texto enriquecido en foro (FORO-01) #}
  330. <script src="https://cdn.tiny.cloud/1/nbx8er3s9hgvqhw2lx3l2bs2capgnkoixfadqf32ctt120vz/tinymce/6/tinymce.min.js" referrerpolicy="origin"></script>
  331. {# Inicialización de TinyMCE para campos de foro #}
  332. <script>
  333. document.addEventListener('DOMContentLoaded', function() {
  334. tinymce.init({
  335. branding: false,
  336. selector: 'textarea.wysiwyg', // Apunta a todos los textareas con nuestra clase
  337. plugins: 'lists link autoresize',
  338. toolbar: 'undo redo | bold italic | bullist numlist | link',
  339. menubar: false,
  340. language: 'es',
  341. height: 500,
  342. autoresize_bottom_margin: 20,
  343. content_style: 'body { font-family: Arial, sans-serif; }',
  344. setup: function (editor) {
  345. // Compatibilidad con validación existente
  346. editor.on('change', function () {
  347. editor.save(); // Sincronizar contenido con textarea original
  348. });
  349. }
  350. });
  351. });
  352. </script>
  353. </body>
  354. </html>