adminFooter.ftl
1 <#--
2 Macro: adminFooter
3
4 Description: Generates the footer section, including documentation and source code links, a Lutece logo, and a version number.
5
6 Parameters:
7 - version : The version number
8 -->
9 <#macro adminFooter closeMain=true >
10 <!-- footer menu -->
11 <footer class="lutece-main-footer footer footer-transparent d-print-none">
12 <div class="container-fluid">
13 <div class="row text-center align-items-center flex-row-reverse">
14 <div class="col-lg-auto ms-lg-auto">
15 <ul class="list-inline mb-0">
16 <li class="list-inline-item nav-item "><a href="https://lutece.paris.fr/support/jsp/site/Portal.jsp?page=wiki" class="nav-link">Documentation</a></li>
17 <li class="list-inline-item nav-item "><a href="https://github.com/lutece-platform/" target="_blank" class="nav-link" rel="noopener">Source code</a></li>
18 </ul>
19 </div>
20 <div class="col-12 col-lg-auto mt-3 mt-lg-0">
21 <ul class="nav list-inline mb-0">
22 <li class="list-inline-item nav-item">
23 <a class="nav-link d-flex align-items-center" href="https://lutece.paris.fr" target="lutece" title="#i18n{portal.site.portal_footer.labelPortal}">
24 <span class="me-2">${site_name}</span>
25 <img src="themes/admin/shared/images/poweredby.svg" style="height:15px" class="img-fluid theme-invert" alt="#i18n{portal.site.portal_footer.labelMadeBy}">
26 <span class="visually-hidden">LUTECE</span>
27 <span class="text-muted ms-2" rel="noopener">version ${version}</span>
28 </a>
29 </li>
30 </ul>
31 </div>
32 </div>
33 </div>
34 </footer>
35 <!-- Included JS Files -->
36 <!-- Le javascript -->
37 <!-- ============================================================== -->
38 <!-- Placed at the end of the document so the pages load faster -->
39 <@coreAdminJSLinks />
40 ${javascript_files}
41 </div><!-- Close wrapper -->
42 </#macro>
43 <#--
44 Macro: adminSiteFooter
45
46 Description: Footer for site Admin
47 Parameters:
48 -
49 -->
50 <#macro adminSiteFooter >
51 <#assign siteFooter = .get_optional_template('../../../../../skin/site/portal_footer.html')>
52 <#if siteFooter.exists><@siteFooter.include /></#if>
53 <!-- A modal dialog containing a form -->
54 <dialog id="addPortletDialog" class="lutece-dialog" aria-labelledby="portletModalLabel" aria-hidden="true" tabindex="-1">
55 <div class="lutece-dialog lutece-dialog-fullscreen">
56 <div class="lutece-dialog-content">
57 <div class="lutece-dialog-header">
58 <h2 class="lutece-dialog-title h4 text-dark" id="portletModalLabel">#i18n{portal.site.portletType.labelCreate}</h2>
59 <button type="button" class="btn btn-link btn-cancel text-dark" aria-label="#i18n{portal.util.labelCancel}"><@icon style="x"/></button>
60 </div>
61 <div class="lutece-dialog-body">
62 <form action="jsp/admin/DoCreatePortlet.jsp" type="get">
63 <div class="container">
64 <div id='portlet_type_id' class="row row-cols-1 row-cols-sm-2 row-cols-md-3 row-cols-lg-4"></div>
65 </div>
66 <div class="d-flex justify-content-center">
67 <button type="button" class="btn btn-secondary btn-cancel" value="cancel" >
68 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-x"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M18 6l-12 12" /><path d="M6 6l12 12" /></svg> #i18n{portal.util.labelClose}</button>
69 </div>
70 </form>
71 </div>
72 <div>
73 </div>
74 </div>
75 </div>
76 </dialog>
77 <script type="module">
78 document.addEventListener( "DOMContentLoaded", function(){
79 const parentPortletTypeNodes = window.parent.document.querySelectorAll( '#offcanvas-body-portlet-type-wrapper ul li' );
80 const dialogPortletTypes = document.getElementById( 'portlet_type_id' );
81 const portletDialog = document.querySelector( '#addPortletDialog' );
82
83 parentPortletTypeNodes.forEach( ( item ) => {
84 const divType = document.createElement('div')
85 divType.classList.add('col')
86 const aType = document.createElement('a')
87 aType.classList.add('btn', 'btn-secondary', 'btn-lg', 'btn-block', 'btn-new-portlet', 'py-5', 'px-0' , 'my-3', 'd-flex', 'align-items-center' )
88 aType.setAttribute( 'href', item.dataset.portletTypeHref )
89 const spanType = document.createElement('span')
90 spanType.classList.add( 'px-2', 'text-left', 'truncate' )
91 spanType.innerText = item.dataset.portletTypeName
92 const iconType = document.createElement('i')
93 iconType.classList.add('ti', <#noparse>`ti-${item.dataset.portletTypeIcon}`</#noparse> ,'fs-1','d-block','ps-2','pl-2')
94 aType.appendChild( iconType );
95 aType.appendChild( spanType );
96 divType.appendChild( aType );
97 dialogPortletTypes.appendChild( divType );
98 })
99
100 //
101 const columnList = document.querySelectorAll( '.lutece-admin-column' );
102 columnList.forEach( ( col ) => {
103 const colOutList = col.querySelectorAll( '.lutece-admin-column-outline' );
104 colOutList.forEach( ( colOut ) => {
105 if( colOut.textContent.trim() === '' ){
106 colOut.textContent= '';
107 }
108 })
109 })
110
111 // "Show the dialog" button opens the <dialog> modally
112 const btnPortletList = document.querySelectorAll( '[data-bs-toggle="modal"]' );
113 btnPortletList.forEach( ( btnPortlet ) => {
114 btnPortlet.addEventListener( 'click', ( event ) => {
115 const portlet = event.currentTarget
116 const portletColumn = portlet.dataset.portletColumn
117 let portletOrder = portlet.dataset.portletOrder
118 if( portletOrder === '' ){
119 const lastPortlet = document.querySelector(<#noparse>`#lutece-column-${portletColumn} .lutece-admin-column-outline .lutece-admin-portlet:last-child .lutece-admin-toolbar</#noparse>`)
120 if( lastPortlet != null ){
121 portletOrder = parseInt( document.querySelector(<#noparse>`#lutece-column-${portletColumn} .lutece-admin-column-outline .lutece-admin-portlet:last-child .lutece-admin-toolbar</#noparse>`).dataset.portletOrder ) + 1
122 } else {
123 portletOrder = 1
124 }
125 }
126 dialogPortletTypes.childNodes.forEach( ( item ) => {
127 let itemLnk = item.firstElementChild;
128 let itemHref = itemLnk.getAttribute('href');
129 <#noparse>itemLnk.setAttribute('href',`${itemHref}&portlet_column=${portletColumn}&portlet_order=${portletOrder}`)</#noparse>
130 })
131 portletDialog.showModal()
132 })
133 })
134
135 const btnCloseDialogList = document.querySelectorAll( '.btn-cancel' );
136 btnCloseDialogList.forEach( ( btnCloseDialog ) => {
137 btnCloseDialog.addEventListener( 'click', ( event ) => {
138 portletDialog.close()
139 })
140 })
141
142 });
143
144 import {
145 LuteceDraggable
146 } from './themes/shared/modules/luteceDraggable.js';
147
148 const root = document.querySelector(":root");
149 root.style.setProperty("--column-empty-text", `"#i18n{portal.site.portletType.labelCreateColumn}"`);
150
151 const containers = document.querySelectorAll('#main .lutece-admin-column-outline');
152 const draggables = Array.from(containers).flatMap(container => [...container.children]);
153 const AdminHomeDraggable = new LuteceDraggable( draggables, containers);
154
155 async function updatePortlet( portletId, col, order ){
156 <#noparse>const response = await fetch( `jsp/admin/site/DoModifyPortletPosition.jsp?portlet_id=${portletId}&column=${col}&order=${order}` );</#noparse>
157 }
158
159 AdminHomeDraggable.on('dragstart', (event) => {
160 root.style.setProperty( "--column-empty-text", `"Drop moi ici !"` );
161 });
162
163 AdminHomeDraggable.on('dragover', (event) => {
164 //event.currentTarget.style.setProperty( "opacity", ".5" );
165 });
166
167 AdminHomeDraggable.on( 'dragend', (event) => {
168 root.style.setProperty("--column-empty-text", `"#i18n{portal.site.portletType.labelCreateColumn}"`);
169 const dragContainer = event.target.closest( '.lutece-draggable-container' );
170 const newCol = dragContainer.dataset.portletColumn;
171 const portletId = event.currentTarget.firstElementChild.dataset.portletId
172 let newOrder= 1
173 if ( event.currentTarget.nextSibling != null ){
174 newOrder = parseInt( event.currentTarget.nextSibling.firstElementChild.dataset.portletOrder )
175 if( newOrder > 1 ){ newOrder-- }
176 } else if ( event.currentTarget.previousSibling != null ){
177 newOrder = parseInt( event.currentTarget.previousSibling.previousSibling.firstElementChild.dataset.portletOrder )
178 newOrder++
179 }
180
181 event.currentTarget.firstElementChild.firstElementChild.textContent = newOrder
182 event.currentTarget.firstElementChild.firstElementChild.dataset.portletOrder = newOrder
183 event.currentTarget.firstElementChild.firstElementChild.dataset.portletColumn = newCol
184
185 if( dragContainer.textContent.trim() === '' ){ dragContainer.textContent = '' }
186
187 updatePortlet( portletId, newCol, newOrder )
188 });
189 </script>
190 </#macro>