layout.html.twig 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. {# layout.html.twig #}
  2. {# Base layout #}
  3. {% if app['combodo.current_user'] is defined and app['combodo.current_user'] is not null %}
  4. {% set bUserConnected = true %}
  5. {% set sUserFullname = app['combodo.current_user'].Get('first_name') ~ ' ' ~ app['combodo.current_user'].Get('last_name') %}
  6. {% set sUserEmail = app['combodo.current_user'].Get('email') %}
  7. {% set sUserPhotoUrl = app['combodo.current_contact.photo_url'] %}
  8. {% else %}
  9. {% set bUserConnected = false %}
  10. {% set sUserFullname = '' %}
  11. {% set sUserEmail = '' %}
  12. {% set sUserPhotoUrl = app['combodo.portal.base.absolute_url'] ~ 'img/user-profile-default-256px.png' %}
  13. {% endif %}
  14. <!doctype html>
  15. <html>
  16. <head>
  17. <meta charset="utf-8">
  18. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  19. <meta name="viewport" content="width=device-width, initial-scale=1">
  20. {# This block can be used to add your own meta tags by extending the default template #}
  21. {% block pPageExtraMetas %}
  22. {% endblock %}
  23. <title>{% block pPageTitle %}{% if sPageTitle is defined and sPageTitle is not null %}{{ sPageTitle }} - {{ constant('ITOP_APPLICATION') }}{% else %}{{ 'Page:DefaultTitle'|dict_s }}{% endif %}{% endblock %}</title>
  24. <link rel="shortcut icon" href="{{ app['combodo.absolute_url'] }}images/favicon.ico?itopversion=$ITOP_VERSION$" />
  25. {% block pPageStylesheets %}
  26. {# First bootstrap core, lib themes, then bootstrap theme, portal adjustements #}
  27. <link href="{{ app['combodo.portal.base.absolute_url'] }}lib/bootstrap/css/bootstrap.min.css" rel="stylesheet">
  28. {# - Bootstrap Datetime picker #}
  29. <link href="{{ app['combodo.portal.base.absolute_url'] }}lib/bootstrap-datetimepicker/css/bootstrap-datetimepicker.min.css" rel="stylesheet">
  30. {# - Datatables #}
  31. <link href="{{ app['combodo.portal.base.absolute_url'] }}lib/datatables/css/dataTables.bootstrap.min.css" rel="stylesheet">
  32. <link href="{{ app['combodo.portal.base.absolute_url'] }}lib/datatables/css/fixedHeader.bootstrap.min.css" rel="stylesheet">
  33. <link href="{{ app['combodo.portal.base.absolute_url'] }}lib/datatables/css/responsive.bootstrap.min.css" rel="stylesheet">
  34. <link href="{{ app['combodo.portal.base.absolute_url'] }}lib/datatables/css/scroller.bootstrap.min.css" rel="stylesheet">
  35. <link href="{{ app['combodo.portal.base.absolute_url'] }}lib/datatables/css/select.bootstrap.min.css" rel="stylesheet">
  36. <link href="{{ app['combodo.portal.base.absolute_url'] }}lib/datatables/css/select.dataTables.min.css" rel="stylesheet">
  37. {# - Font Combodo #}
  38. <link href="{{ app['combodo.absolute_url'] }}css/font-combodo/font-combodo.css" rel="stylesheet">
  39. {# - Font awesome #}
  40. <link href="{{ app['combodo.portal.base.absolute_url'] }}lib/font-awesome/css/font-awesome.min.css" rel="stylesheet">
  41. {# - Misc libs #}
  42. <link href="{{ app['combodo.portal.base.absolute_url'] }}lib/typeahead/css/typeaheadjs.bootstrap.css" rel="stylesheet">
  43. <link href="{{ app['combodo.absolute_url'] }}css/magnific-popup.css" rel="stylesheet">
  44. {# - Bootstrap theme #}
  45. <link href="{{ app['combodo.portal.instance.conf'].properties.themes.bootstrap }}" rel="stylesheet" id="css_bootstrap_theme">
  46. {# - Portal adjustments for BS theme #}
  47. <link href="{{ app['combodo.portal.instance.conf'].properties.themes.portal }}" rel="stylesheet" id="css_portal">
  48. {# Custom CSS that is supposed to do adjustments to the portal #}
  49. {% if app['combodo.portal.instance.conf'].properties.themes.custom is defined %}
  50. <link href="{{ app['combodo.portal.instance.conf'].properties.themes.custom }}" rel="stylesheet">
  51. {% endif %}
  52. {# Others CSS that will come after the theme/portal/custom, in an undefined order #}
  53. {% if app['combodo.portal.instance.conf'].properties.themes.others is defined %}
  54. {% for theme in app['combodo.portal.instance.conf'].properties.themes.others %}
  55. <link href="{{ theme }}" rel="stylesheet">
  56. {% endfor %}
  57. {% endif %}
  58. {% endblock %}
  59. {% block pPageScripts %}
  60. <script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] }}lib/jquery/jquery-1.11.3.min.js"></script>
  61. <script type="text/javascript" src="{{ app['combodo.absolute_url'] }}js/jquery-ui-1.10.3.custom.min.js"></script>
  62. <script type="text/javascript" src="{{ app['combodo.absolute_url'] }}js/jquery.magnific-popup.min.js"></script>
  63. <script type="text/javascript" src="{{ app['combodo.absolute_url'] }}js/jquery.fileupload.js"></script>
  64. <script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] }}lib/bootstrap/js/bootstrap.min.js"></script>
  65. <script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] }}lib/latinise/latinise.min.js"></script>
  66. {# Visible.js to check if an element is visible on screen #}
  67. <script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] }}lib/jquery-visible/js/jquery.visible.min.js"></script>
  68. {# Base64.js #}
  69. <script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] }}lib/jquery-base64/js/jquery.base64.min.js"></script>
  70. {# Moment.js #}
  71. <script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] }}lib/moment/js/moment.min.js"></script>
  72. {# Datatables #}
  73. <script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] }}lib/datatables/js/jquery.dataTables.min.js"></script>
  74. <script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] }}lib/datatables/js/dataTables.bootstrap.min.js"></script>
  75. <script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] }}lib/datatables/js/dataTables.fixedHeader.min.js"></script>
  76. <script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] }}lib/datatables/js/dataTables.responsive.min.js"></script>
  77. <script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] }}lib/datatables/js/dataTables.scroller.min.js"></script>
  78. <script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] }}lib/datatables/js/dataTables.select.min.js"></script>
  79. <script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] }}lib/datatables/js/datetime-moment.js"></script>
  80. <script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] }}js/dataTables.accentNeutraliseForFilter.js"></script>
  81. {# CKEditor files for HTML WYSIWYG #}
  82. <script type="text/javascript" src="{{ app['combodo.absolute_url'] }}js/ckeditor/ckeditor.js"></script>
  83. <script type="text/javascript" src="{{ app['combodo.absolute_url'] }}js/ckeditor/adapters/jquery.js"></script>
  84. {# Date-time picker for Bootstrap #}
  85. <script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] }}lib/bootstrap-datetimepicker/js/bootstrap-datetimepicker.min.js"></script>
  86. {# Typeahead files for autocomplete #}
  87. <script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] }}lib/typeahead/js/bloodhound.min.js"></script>
  88. <script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] }}lib/typeahead/js/typeahead.bundle.min.js"></script>
  89. <script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] }}lib/typeahead/js/typeahead.jquery.min.js"></script>
  90. <script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] }}lib/handlebars/js/handlebars.min-768ddbd.js"></script>
  91. {# Form files #}
  92. <script type="text/javascript" src="{{ app['combodo.absolute_url'] }}js/form_handler.js"></script>
  93. <script type="text/javascript" src="{{ app['combodo.absolute_url'] }}js/form_field.js"></script>
  94. <script type="text/javascript" src="{{ app['combodo.absolute_url'] }}js/subform_field.js"></script>
  95. <script type="text/javascript" src="{{ app['combodo.absolute_url'] }}js/field_set.js"></script>
  96. {# Form files for portal #}
  97. <script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] }}js/portal_form_handler.js"></script>
  98. <script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] }}js/portal_form_field.js"></script>
  99. <script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] }}js/portal_form_field_html.js"></script>
  100. {% endblock %}
  101. </head>
  102. <body class="{% block pPageBodyClass %}{% endblock %}">
  103. {% block pPageBodyWrapper %}
  104. {% block pNavigationWrapper %}
  105. {# Topbar navigation menu for mobile screens #}
  106. <nav class="navbar navbar-fixed-top navbar-default visible-xs" id="topbar" role="navigation">
  107. <div class="container-fluid">
  108. <div class="navbar-header">
  109. <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar">
  110. <span class="icon-bar"></span>
  111. <span class="icon-bar"></span>
  112. <span class="icon-bar"></span>
  113. </button>
  114. <a class="navbar-brand pull-right" href="{{ app.url_generator.generate('p_home') }}">
  115. {% if app['combodo.portal.instance.conf'].properties.logo is not null %}
  116. <img src="{{ app['combodo.portal.instance.conf'].properties.logo }}" alt="{{ app['combodo.portal.instance.conf'].properties.name|dict_s }}" />
  117. {% else %}
  118. iTop
  119. {% endif %}
  120. </a>
  121. <p class="navbar-text">
  122. <a class="navbar-link user_infos" href="{{ app.url_generator.generate('p_user_profile_brick') }}">
  123. <span class="user_photo" style="background-image: url('{{ sUserPhotoUrl }}');"></span>
  124. <span class="user_fullname">{{ sUserFullname }}</span>
  125. </a>
  126. </p>
  127. </div>
  128. <div class="collapse navbar-collapse" id="navbar">
  129. <ul class="nav navbar-nav">
  130. {% block pNavigationTopBricks %}
  131. <li class="{% if oBrick is not defined %}active{% endif %}">
  132. <a href="{{ app.url_generator.generate('p_home') }}">
  133. <span class="brick_icon fa fa-home fa-2x"></span>
  134. {{ 'Page:Home'|dict_s }}
  135. </a>
  136. </li>
  137. {% for brick in app['combodo.portal.instance.conf'].bricks_ordering.navigation_menu %}
  138. {% if brick.GetActive and brick.GetVisibleNavigationMenu and brick.GetRouteName is not null %}
  139. <li class="{% if oBrick is defined and brick.id == oBrick.id %}active{% endif %}">
  140. <a href="{{ app.url_generator.generate(brick.GetRouteName, {sBrickId: brick.GetId}) }}{% if app['combodo.portal.instance.routes'][brick.GetRouteName]['hash'] is defined %}#{{ app['combodo.portal.instance.routes'][brick.GetRouteName]['hash'] }}{% endif %}" {% if app['combodo.portal.instance.routes'][brick.GetRouteName]['navigation_menu_attr'] is defined %}{% for key, value in app['combodo.portal.instance.routes'][brick.GetRouteName]['navigation_menu_attr'] %} {{ key }}="{{ value }}"{% endfor %}{% endif %} {% if brick.GetModal %}data-toggle="modal" data-target="#modal-for-all"{% endif %}>
  141. <span class="brick_icon {{ brick.GetDecorationClassNavigationMenu }}"></span>
  142. {{ brick.GetTitleNavigationMenu|dict_s }}
  143. </a>
  144. </li>
  145. {% endif %}
  146. {% endfor %}
  147. {% endblock %}
  148. {% if bUserConnected %}
  149. <li role="separator" class="divider"></li>
  150. <li><a href="{{ app.url_generator.generate('p_user_profile_brick') }}"><span class="brick_icon fa fa-user fa-2x fa-fw"></span>{{ 'Brick:Portal:UserProfile:Navigation:Dropdown:MyProfil'|dict_s }}</a></li>
  151. {% for aPortal in app['combodo.portal.instance.conf'].portals %}
  152. {% if aPortal.id != app['combodo.portal.instance.conf'].properties.id %}
  153. {% set sIconClass = (aPortal.id == 'backoffice') ? 'fa-list-alt' : 'fa-external-link' %}
  154. <li><a href="{{ aPortal.url }}" target="_blank"><span class="brick_icon fa {{ sIconClass }} fa-2x fa-fw"></span>{{ aPortal.label|dict_s }}</a></li>
  155. {% endif %}
  156. {% endfor %}
  157. {# We display the separator only if the user has more then 1 portal. Meaning default portal + console or several portal at least #}
  158. {% if app['combodo.portal.instance.conf'].portals|length > 1 %}
  159. <li role="separator" class="divider"></li>
  160. {% endif %}
  161. <li><a href="{{ app['combodo.absolute_url'] }}pages/logoff.php"><span class="brick_icon fa fa-sign-out fa-2x fa-fw"></span>{{ 'Brick:Portal:UserProfile:Navigation:Dropdown:Logout'|dict_s }}</a></li>
  162. {% endif %}
  163. </ul>
  164. </div>
  165. </div>
  166. </nav>
  167. {# Sidebar navigation menu for normal screens #}
  168. <nav class="navbar-default hidden-xs col-sm-3 col-md-2" id="sidebar" role="navigation">
  169. <div class="user_card bg-primary">
  170. <div class="user_photo" style="background-image: url('{{ sUserPhotoUrl }}');">
  171. </div>
  172. <div class="user_infos">
  173. <div class="user_fullname">{{ sUserFullname }}</div>
  174. <div class="user_email dropdown">
  175. <a href="#" class="dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" id="user_options">
  176. {{ sUserEmail }}
  177. <span class="caret"></span>
  178. </a>
  179. <ul class="dropdown-menu user_options" aria-labelledby="user_options">
  180. <li><a href="{{ app.url_generator.generate('p_user_profile_brick') }}"><span class="brick_icon fa fa-user fa-lg fa-fw"></span>{{ 'Brick:Portal:UserProfile:Navigation:Dropdown:MyProfil'|dict_s }}</a></li>
  181. {% for aPortal in app['combodo.portal.instance.conf'].portals %}
  182. {% if aPortal.id != app['combodo.portal.instance.conf'].properties.id %}
  183. {% set sGlyphiconClass = (aPortal.id == 'backoffice') ? 'fa-list-alt' : 'fa-external-link' %}
  184. <li><a href="{{ aPortal.url }}" target="_blank" title="{{ aPortal.label|dict_s }}"><span class="brick_icon fa {{ sGlyphiconClass }} fa-lg fa-fw"></span>{{ aPortal.label|dict_s }}</a></li>
  185. {% endif %}
  186. {% endfor %}
  187. {# We display the separator only if the user has more then 1 portal. Meaning default portal + console or several portal at least #}
  188. {% if app['combodo.portal.instance.conf'].portals|length > 1 %}
  189. <li role="separator" class="divider"></li>
  190. {% endif %}
  191. <li><a href="{{ app['combodo.absolute_url'] }}pages/logoff.php"><span class="brick_icon fa fa-sign-out fa-lg fa-fw"></span>{{ 'Brick:Portal:UserProfile:Navigation:Dropdown:Logout'|dict_s }}</a></li>
  192. </ul>
  193. </div>
  194. </div>
  195. </div>
  196. <div class="menu">
  197. {% block pNavigationSideMenu %}
  198. <ul class="nav navbar-nav">
  199. <li class="{% if oBrick is not defined %}active{% endif %}">
  200. <a href="{{ app.url_generator.generate('p_home') }}">
  201. <span class="brick_icon fa fa-home fa-2x"></span>
  202. {{ 'Page:Home'|dict_s }}
  203. </a>
  204. </li>
  205. {% for brick in app['combodo.portal.instance.conf'].bricks_ordering.navigation_menu %}
  206. {% if brick.GetActive and brick.GetVisibleNavigationMenu and brick.GetRouteName is not null %}
  207. <li class="{% if oBrick is defined and brick.id == oBrick.id %}active{% endif %}">
  208. <a href="{{ app.url_generator.generate(brick.GetRouteName, {sBrickId: brick.GetId}) }}{% if app['combodo.portal.instance.routes'][brick.GetRouteName]['hash'] is defined %}#{{ app['combodo.portal.instance.routes'][brick.GetRouteName]['hash'] }}{% endif %}" {% if app['combodo.portal.instance.routes'][brick.GetRouteName]['navigation_menu_attr'] is defined %}{% for key, value in app['combodo.portal.instance.routes'][brick.GetRouteName]['navigation_menu_attr'] %} {{ key }}="{{ value }}"{% endfor %}{% endif %} {% if brick.GetModal %}data-toggle="modal" data-target="#modal-for-all"{% endif %}>
  209. <span class="brick_icon {{ brick.GetDecorationClassNavigationMenu }}"></span>
  210. {{ brick.GetTitleNavigationMenu|dict_s }}
  211. </a>
  212. </li>
  213. {% endif %}
  214. {% endfor %}
  215. </ul>
  216. {% endblock %}
  217. </div>
  218. {% if app['combodo.portal.instance.conf'].properties.logo is not null %}
  219. <div class="logo">
  220. {# This is a debug helper to know in which screen size we are #}
  221. {% if app['debug'] %}
  222. <div style="position: fixed; bottom: 0px; left: 0px; z-index: 9999;">Debug : Taille <span class="hidden-sm hidden-md hidden-lg">XS</span><span class="hidden-xs hidden-md hidden-lg">SM</span><span class="hidden-xs hidden-sm hidden-lg">MD</span><span class="hidden-xs hidden-sm hidden-md">LG</span></div>
  223. {% endif %}
  224. <a href="{{ app.url_generator.generate('p_home') }}" title="{{ app['combodo.portal.instance.conf'].properties.name|dict_s }}">
  225. <img src="{{ app['combodo.portal.instance.conf'].properties.logo }}" alt="{{ app['combodo.portal.instance.conf'].properties.name|dict_s }}" />
  226. </a>
  227. </div>
  228. {% endif %}
  229. </nav>
  230. {% endblock %}
  231. {% block pMainWrapper %}
  232. <div class="container-fluid" id="main-wrapper">
  233. <div class="row">
  234. <div class="col-xs-12 col-sm-9 col-md-10 col-sm-offset-3 col-md-offset-2">
  235. <section class="row" id="main-header">
  236. {% block pMainHeader %}
  237. {% endblock %}
  238. </section>
  239. <section class="row" id="main-content">
  240. {% block pMainContent %}
  241. {% endblock %}
  242. </section>
  243. </div>
  244. </div>
  245. </div>
  246. {% endblock %}
  247. <footer id="footer-wrapper">
  248. {% block pPageFooter%}
  249. <a href="http://www.combodo.com">iTop &copy; {{ "now"|date('Y') }}</a>
  250. {% endblock %}
  251. </footer>
  252. <div class="modal fade" id="modal-for-all" role="dialog">
  253. <div class="modal-dialog modal-lg" role="document">
  254. <div class="modal-content">
  255. {% include 'itop-portal-base/portal/src/views/helpers/loader.html.twig' %}
  256. </div>
  257. </div>
  258. </div>
  259. <div class="modal fade" id="modal-for-alert" role="dialog">
  260. <div class="modal-dialog" role="document">
  261. <div class="modal-content">
  262. <div class="modal-header">
  263. <button type="button" class="close" data-dismiss="modal" aria-label="{{ 'Portal:Button:Close'|dict_s }}"><span aria-hidden="true">&times;</span></button>
  264. <h4 class="modal-title"></h4>
  265. </div>
  266. <div class="modal-body">
  267. <div class="alert">
  268. </div>
  269. <div class="text-right">
  270. <button type="button" class="btn btn-primary" data-dismiss="modal">{{ 'Portal:Button:Close'|dict_s }}</button>
  271. </div>
  272. </div>
  273. </div>
  274. </div>
  275. </div>
  276. <div id="page_overlay" class="global_overlay">
  277. <div class="overlay_content">
  278. {% include 'itop-portal-base/portal/src/views/helpers/loader.html.twig' %}
  279. </div>
  280. </div>
  281. {% endblock %}
  282. {% block pPageLiveScripts %}
  283. <script type="text/javascript">
  284. var GetAbsoluteUrlAppRoot = function()
  285. {
  286. return '{{ app['combodo.absolute_url'] }}';
  287. };
  288. var AddParameterToUrl = function(sUrl, sParamName, sParamValue)
  289. {
  290. sUrl += (sUrl.split('?')[1] ? '&':'?') + sParamName + '=' + sParamValue;
  291. return sUrl;
  292. };
  293. var GetContentLoaderTemplate = function()
  294. {
  295. return '<div class="content_loader"><div class="icon glyphicon glyphicon-refresh"></div><div class="message">{{ 'Page:PleaseWait'|dict_s }}</div></div>';
  296. }
  297. $(document).ready(function(){
  298. {% block pPageReadyScripts %}
  299. // Hack to enable a same modal to load content from different urls
  300. $('body').on('hidden.bs.modal', '.modal#modal-for-all', function () {
  301. $(this).removeData('bs.modal');
  302. $(this).find('.modal-content').html(GetContentLoaderTemplate());
  303. });
  304. // Hack to enable multiple modals by making sure the .modal-open class is set to the <body> when there is at least one modal open left
  305. $('body').on('hidden.bs.modal', function () {
  306. if($('.modal.in').length > 0)
  307. {
  308. $('body').addClass('modal-open');
  309. }
  310. });
  311. // Hide tooltips when a modal is opening, otherwise it might be overlapping it
  312. $('body').on('show.bs.modal', function () {
  313. $(this).find('.tooltip.in').tooltip('hide');
  314. });
  315. // Display a error message on modal if the content could not be loaded.
  316. // Note : As of now, we can't display a more detailled message based on the response because Bootstrap doesn't pass response data with the loaded event.
  317. $('body').on('loaded.bs.modal', function (oEvent) {
  318. var sModalContent = $(oEvent.target).find('.modal-content').html();
  319. if( (sModalContent === '') || (sModalContent.replace(/[\n\r\t]+/g, '') === GetContentLoaderTemplate()) )
  320. {
  321. $(oEvent.target).find('.modal-content').html($('#modal-for-alert .modal-content').html());
  322. $(oEvent.target).find('.modal-content .modal-header .modal-title').text('{{ 'Error:HTTP:500'|dict_s }}');
  323. $(oEvent.target).find('.modal-content .modal-body .alert').addClass('alert-danger').text('{{ 'Error:XHR:Fail'|dict_s }}');
  324. }
  325. });
  326. {% endblock %}
  327. });
  328. </script>
  329. {% endblock %}
  330. </body>
  331. </html>