Spiagge libere
Numerose bandiere blu certificano la qualità delle acque e dei servizi, 180 chilometri di costa, più di 20 località che si affacciano sul Mare Adriatico, oltre al porto marittimo di Ancona e 9 porti turistici: questa è l'ampia offerta certificata di qualità per una vacanza al mare nelle Marche.
Lunghi litorali di sabbia finissima, acque limpide che si rifrangono sugli scogli, piccole e raccolte baie immerse in paesaggi incontaminati. Il mare delle Marche è a due passi dai centri storici: un alternarsi di lunghe spiagge e ripide pareti rocciose che precipitano nelle acque azzurre e, come sfondo, le dolci colline che caratterizzano l’immediato entroterra.
Leggi tutto
Riduci
Si è verificato un errore nell'elaborarazione del modello.
The following has evaluated to null or missing: ==> curExtraSearchField.inputAspect [in template "20097#20123#371648" at line 108, column 94] ---- Tip: It's the step after the last dot that caused this error, not those before it. ---- Tip: If the failing expression is known to legally refer to something that's sometimes null or missing, either specify a default value like myOptionalVar!myDefault, or use <#if myOptionalVar??>when-present<#else>when-missing</#if>. (These only cover the last step of the expression; to cover the whole expression, use parenthesis: (myOptionalVar.foo)!myDefault, (myOptionalVar.foo)?? ---- ---- FTL stack trace ("~" means nesting-related): - Failed at: #if curExtraSearchField.inputAspect =... [in template "20097#20123#371648" at line 108, column 89] - Reached through: @ricerca_components["ricerca-form"] s... [in template "20097#20123#371648" at line 58, column 33] ----
1<#-- instance id -->
2<#assign instanceId = themeDisplay.getPortletDisplay().getId()?keep_after("INSTANCE_") />
3
4<#-- Ricavo servizi utili in seguito -->
5<#-- Servizio per i layout di pagina -->
6<#assign layoutLocalService = serviceLocator.findService("com.liferay.portal.kernel.service.LayoutLocalService") />
7<#-- Servizio utile per ricavare i documenti della DM -->
8<#assign dlFileEntryLocalService = serviceLocator.findService('com.liferay.document.library.kernel.service.DLFileEntryLocalService') />
9
10<#-- Etichette -->
11<#-- attrazioni -->
12<#assign label_attrazione = languageUtil.get(locale, "label_attrazione") />
13<#assign label_attrazioni = languageUtil.get(locale, "label_attrazioni") />
14<#assign ricerca_extra_field_place = languageUtil.get(locale, "ricerca_extra_field_place") />
15<#assign ricerca_extra_field_place_placeholder = languageUtil.get(locale, "ricerca_extra_field_place_placeholder") />
16<#assign label_chiama_telefono = languageUtil.get(locale, "label_chiama_telefono") />
17<#assign label_invia_email_a = languageUtil.get(locale, "label_invia_email_a") />
18<#assign label_e_mail = languageUtil.get(locale, "label_e_mail") />
19<#assign label_sito_web = languageUtil.get(locale, "label_sito_web") />
20<#assign label_vai_sito_web = languageUtil.get(locale, "label_vai_sito_web") />
21<#-- generiche -->
22<#assign label_filtro_cosa = languageUtil.get(locale, "label_filtro_cosa") />
23<#assign label_placeholder_filtro_cosa = languageUtil.get(locale, "label_placeholder_filtro_cosa") />
24<#assign label_button_cerca = languageUtil.get(locale, "label_button_cerca") />
25<#assign label_button_cerca_su_mappa = languageUtil.get(locale, "label_button_cerca_su_mappa") />
26<#assign label_categorie = languageUtil.get(locale, "label_categorie") />
27<#assign label_seleziona = languageUtil.get(locale, "label_seleziona") />
28<#assign label_vai_dettaglio = languageUtil.get(locale, "label_vai_dettaglio") />
29<#assign label_leggi_tutto = languageUtil.get(locale, "label_leggi_tutto") />
30<#assign label_aggiungi_lista_viaggio = languageUtil.get(locale, "label_aggiungi_lista_viaggio") />
31<#assign label_scarica_formato_csv = languageUtil.get(locale, "label_scarica_formato_csv") />
32
33<#-- Inclusione widget template con macros e relativi servizi -->
34<#include "${templatesPath}/RICERCA_COMMON_MACROS" />
35
36<#-- campi personalizzati -->
37<#-- ricavo il valore del vocabolario DESTINAZIONI, che serve per recuperare le relative categorie -->
38<#assign DESTINAZIONI_VOCABULARY_ID = "" />
39<#if validator.isNotNull(themeDisplay.getLayout().getGroup().getExpandoBridge()) >
40 <#if (( themeDisplay.getLayout().getGroup().getExpandoBridge().hasAttribute("destinazioni_vocabulary_id"))) >
41 <#if themeDisplay.getLayout().getGroup().getExpandoBridge().getAttribute("destinazioni_vocabulary_id")??>
42 <#assign DESTINAZIONI_VOCABULARY_ID = themeDisplay.getLayout().getGroup().getExpandoBridge().getAttribute("destinazioni_vocabulary_id")/>
43 </#if>
44 </#if>
45</#if>
46
47<#-- html -->
48<div class="col-12">
49 <div class="search-template search-template-attrazioni">
50
51 <#-- da qui inizia l'html così come da mockup: solo modalita desktop -->
52 <div class="search-form-container search-form-container-desktop py-3">
53 <#-- aggiunta classe m-0 -->
54 <div class="row m-0">
55 <div class="col-12">
56
57 <#-- form con ricerca testuale, per data, e pulsanti -->
58 <@ricerca_components["ricerca-form"] searchUrl="${searchUrl}" method="get" id="search-attrazioni-form-desktop">
59
60 <#-- contenitore con sfondo -->
61 <div class="search-form">
62
63 <#-- campo hidden per ricerca senza risultati -->
64 <input type="hidden" name="searchSubmit" value="true">
65
66 <#-- campo hidden per ordinamento: non usato -->
67 <#--
68 <input type="hidden" value="${searchInput.orderBy}" name="orderBy" id="orderByInputHiddenDesktop">
69 -->
70
71 <#-- campo hidden per cambio vista: non usato -->
72 <#--
73 <input type="hidden" value="${viewTypeKey}${ricercaPortletConfig.configViewParamsSeparator}${viewTypeValue}" name="viewParams" id="input-viewParams-viewType">
74 -->
75
76 <#-- inizio barra filtri orizzontatli -->
77 <div class="form-row align-items-center">
78
79 <#if ricercaPortletConfig.configInputqTitleLabel != "">
80 <#assign label_filtro_cosa = ricercaPortletConfig.configInputqTitleLabel />
81 </#if>
82
83 <#-- campo di ricerca testuale -->
84 <div class="col-12 <#if searchInput.extraSearchFields?has_content || (ricercaPortletConfig.configShowCategoriesFilterInSearchbar == true)>col-md mb-md-2<#else>col-md</#if> text-uppercase search-form-title">
85 <label for="input-ricerca-q" class="mb-0">${label_filtro_cosa}</label>
86 <@ricerca_components["ricerca-input-q"] cssClass="form-control"
87 value="${searchInput.q}"
88 placeholder="${label_placeholder_filtro_cosa}">
89 </@>
90 <#if SUGGERIMENTI_RICERCA_CONTENT?? && SUGGERIMENTI_RICERCA_CONTENT !="">
91 ${SUGGERIMENTI_RICERCA_CONTENT}
92 </#if>
93 </div>
94
95 <#-- campo di ricerca per località: campo dinamico prelevato da configurazione -->
96 <#if searchInput.extraSearchFields?has_content>
97 <#list searchInput.extraSearchFields as curExtraSearchField >
98 <#assign curExtraSearchFieldValue = "" />
99 <#if curExtraSearchField.value?has_content>
100 <#assign curExtraSearchFieldValue = curExtraSearchField.value />
101 </#if>
102
103 <#assign curExtraSearchFieldName = curExtraSearchField.fieldNamePrefix+curExtraSearchField.field />
104 <#assign curExtraSearchFieldLabel = curExtraSearchField.fieldLabel />
105
106 <div id="${curExtraSearchFieldName}_container" class="col-12 col-md text-uppercase search-form-extra-field mb-md-2">
107 <label for="${curExtraSearchFieldName}" class="mb-0">${curExtraSearchFieldLabel}</label>
108 <#if curExtraSearchField.inputAspect = "OPTIONS">
109 <select class="form-control" id="${curExtraSearchFieldName}" name="${curExtraSearchFieldName}" value="${curExtraSearchFieldValue}">
110 <option value="">${label_seleziona}</option>
111 <#assign keyName = curExtraSearchField.field />
112 <#assign selectOptions = selectsItemsLabels[keyName] />
113 <#if selectOptions?has_content>
114 <#list selectOptions as opt>
115 <#assign isSelected = "" />
116 <#if curExtraSearchFieldValue?string?lower_case == opt?lower_case>
117 <#assign isSelected = "selected" />
118 </#if>
119 <option value="${opt}" ${isSelected}>${opt}</option>
120 </#list>
121 </#if>
122 </select>
123 <#else>
124 <input type="text" class="form-control" id="${curExtraSearchFieldName}" name="${curExtraSearchFieldName}" value="${curExtraSearchFieldValue}" placeholder="${label_placeholder_filtro_cosa}">
125 </#if>
126 </div>
127
128 <#if curExtraSearchField.autoCompleteUrl?? && curExtraSearchField.autoCompleteUrl != "">
129 <#if curExtraSearchField.inputAspect = "TEXTFIELD">
130 <script>
131 $('#${curExtraSearchFieldName}').autocomplete({
132 paramName: 'q',
133 appendTo: '#${curExtraSearchFieldName}_container',
134 serviceUrl: '${curExtraSearchField.autoCompleteUrl}',
135 dataType: 'json',
136 transformResult: function(response) {
137 return {
138 suggestions: $.map(response.data, function(dataItem) {
139 return dataItem.displayName;
140 })
141 };
142 }
143 });
144 </script>
145 </#if>
146 </#if>
147 </#list>
148 </#if>
149
150 <#if ricercaPortletConfig.configSearchbarCategoriesFieldLabel != "">
151 <#assign label_categorie = ricercaPortletConfig.configSearchbarCategoriesFieldLabel />
152 </#if>
153
154 <#if (ricercaPortletConfig.configShowCategoriesFilterInSearchbar == true)>
155 <#-- visualizzo searchbar categories -->
156 <#if (searchbarCategoriesAsKeyValue?size > 0)>
157 <div class="col-12 col-md text-uppercase search-form-extra-field mb-md-2">
158 <label for="searchbar-select-ricerca-categorie" class="mb-0">${label_categorie}</label>
159 <select name="categoryIdsSearchbar" class="form-control" id="searchbar-select-ricerca-categorie">
160 <option value="">${label_seleziona}</option>
161 <#list searchbarCategoriesAsKeyValue?keys as prop>
162 <#assign isSelected = "" />
163 <#if searchInput.categoryIdsSearchbar?has_content>
164 <#list searchInput.categoryIdsSearchbar as categoryId>
165 <#if categoryId?string == prop>
166 <#assign isSelected = "selected" />
167 </#if>
168 </#list>
169 </#if>
170 <option value="${prop}" ${isSelected}>${searchbarCategoriesAsKeyValue[prop]}</option>
171 </#list>
172 </select>
173 </div>
174 </#if>
175 <#-- visualizzo searchbar extra categories -->
176 <#if categoriesExtraSearchFieldsObject?has_content>
177 <#list categoriesExtraSearchFieldsObject as curVoc>
178 <div class="col-12 col-md text-uppercase search-form-extra-field mb-md-2">
179 <label for="searchbar-select-ricerca-categorie-${curVoc.vocabularyId}" class="mb-0">
180 ${curVoc.vocabularyLabel}
181 </label>
182 <select name="categoryIdsSearchbar" class="form-control" id="searchbar-select-ricerca-categorie-${curVoc.vocabularyId}">
183 <option value="">
184 ${label_seleziona}
185 </option>
186 <#list curVoc.categories?keys as curCat>
187 <#assign isSelected="" />
188 <#if searchInput.categoryIdsSearchbar?has_content>
189 <#list searchInput.categoryIdsSearchbar as categoryId>
190 <#if categoryId?string == curCat>
191 <#assign isSelected="selected" />
192 </#if>
193 </#list>
194 </#if>
195 <option value="${curCat}" ${isSelected}>
196 ${curVoc.categories[curCat]}
197 </option>
198 </#list>
199 </select>
200 </div>
201 </#list>
202 </#if>
203 </#if>
204
205 <#-- pulsante di ricerca -->
206 <div class="col-12 col-md-2 search-form-button">
207 <button type="submit" class="btn-dark btn-block"><span class="fa fa-search mr-1" aria-hidden="true"></span>${label_button_cerca}</button>
208 </div>
209
210 <#-- codice aggiunto per la ricerca geo su mappa, funziona con la variabile impostata a true nell'init -->
211 <#assign latValue="" />
212 <#if searchInput.lat?has_content>
213 <#assign latValue="${searchInput.lat}" />
214 </#if>
215 <#assign lngValue="" />
216 <#if searchInput.lng?has_content>
217 <#assign lngValue="${searchInput.lng}" />
218 </#if>
219 <#assign distanceValue="" />
220 <#if searchInput.distance?has_content>
221 <#assign distanceValue="${searchInput.distance}" />
222 </#if>
223
224 <#-- variabili per gestire accessibilità e visualizzazione mappa, nel caso in cui cè una ricerca geo in atto -->
225 <#assign statusCollapse = "" />
226 <#assign statusCollapseAria = "false" />
227 <#if (searchInput.lat?has_content) && (searchInput.lng?has_content) && (searchInput.distance?has_content)>
228 <#assign statusCollapse = "show" />
229 <#assign statusCollapseAria = "true" />
230 </#if>
231
232 <#-- pulsante per visualizzare la mappa nascosta -->
233 <div class="col-12 col-md-3 search-form-map-button">
234 <button class="container-fluid position-relative transition btn-dark btn-map" type="button" data-toggle="collapse" data-target="#map-container-desktop" aria-expanded="${statusCollapseAria}" aria-controls="map-container-desktop">
235 <span class="position-relative"><span class="fa fa-map-marker mr-1" aria-hidden="true"></span>${label_button_cerca_su_mappa}</span>
236 </button>
237 </div>
238 </div>
239
240 <#-- richiamo taglib mappa: ha il suo html base -->
241 <#-- rich-map: wrapper class for rich marker -->
242 <div id="map-container-desktop" class="rich-map-container collapse my-4 ${statusCollapse}">
243 <@ricerca_components["ricerca-map"]>
244 </@>
245 </div>
246 </div>
247
248 <#-- IMPORTANTE: inizializzo la mappa, da fare subito dopo la chiusura della taglib -->
249 <#-- poichè il form ha un id custom, lo riporto come parametro nell'init -->
250 <script>
251 TglSearchMap.init({
252 centerLatitude: 43.3285259,
253 centerLongitude: 13.0509109,
254 initialZoom: 13,
255 geoSearch: true,
256 searchFormId: "search-attrazioni-form-desktop",
257 submitOnGeoSearch: true
258 });
259 </script>
260
261 <#-- nel caso di una ricerca geo, passo alla mappa i valori impostati nelle variabili sopra create-->
262 <script type="text/javascript">
263 TglSearchMap.setSearchCircle('${latValue}', '${lngValue}', '${distanceValue}');
264 </script>
265
266 </@>
267 </div>
268 </div>
269 </div>
270
271 <#if entries?has_content>
272 <#list entries as curEntry>
273
274 <#-- sezione divisa in due colonne: categorie e lista risultati -->
275 <div id="search-results" class="search-results">
276
277 <#-- aggiunta classe m-0 -->
278 <div class="row m-0">
279
280 <#-- ricerca per categorie -->
281 <#if (ricercaPortletConfig.configSearchByCategories == true) && (ricercaPortletConfig.configShowCategoriesFilterInSidebar == true) && (categoriesAsKeyValue?size > 0)>
282
283 <#-- setto una variabile booleana per far partire gli accordion o aperti o chiusi a seconda della configurazione -->
284 <#assign vocabulariesCollapsed = false />
285 <#if ricercaPortletConfig.configStartWithCollapsedVocabularies?? && ricercaPortletConfig.configStartWithCollapsedVocabularies == true>
286 <#assign vocabulariesCollapsed = true />
287 </#if>
288
289 <#-- modificato da col-md-3 a col-lg-3 -->
290 <div class="col-12 col-lg-3 search-filters-container">
291
292 <div class="search-filters filters-desktop">
293 <#-- caso di ricerca per categorie multiple -->
294 <#if ricercaPortletConfig.configSelectMultipleCategories == true>
295 <#-- caso di visualizzazione di tutte le categorie -->
296 <#if ricercaPortletConfig.allVocabulariesCategories == true>
297 <#-- caso di elenco di categorie e sottocategorie indentate con nome vocabolario - sfrutta macro presente nel template RICERCA_COMMON_MACROS -->
298 <#if ricercaPortletConfig.configAllowVocabulariesLabelFilter == true>
299 <span class="d-flex align-items-center justify-content-between text-uppercase" data-toggle="collapse" data-target="#list-filters" role="button" aria-expanded="true" aria-controls="list-filters">
300 <span>${label_categorie}
301 <#if searchInput.categoryIds?has_content>
302 <span class="badge">${searchInput.categoryIds?size}</span>
303 </#if>
304 </span>
305 </span>
306 <div id="list-filters" class="list-filters collapse show mt-4">
307 <ul class="list-unstyled">
308 <#list hierarchicalVocabulariesOptions as voKey, voValue>
309 <#if voKey == DESTINAZIONI_VOCABULARY_ID>
310 <#-- nn lo visualizzo -->
311 <#else>
312 <li class="mb-2">
313 <a class="accordion-title h4" href="#accordion-content_${voKey}" data-toggle="collapse" aria-expanded="<#if !vocabulariesCollapsed>true<#else>false</#if>">
314 ${voValue.value.label}
315 </a>
316 <div id="accordion-content_${voKey}" class="accordion-content collapse <#if !vocabulariesCollapsed>show</#if>">
317 <@getAllCategoriesDesktop voValue.children />
318 </div>
319 </li>
320 </#if>
321 </#list>
322 </ul>
323 </div>
324 <#else>
325 <#-- caso di elenco categorie e sottocategorie indentate ma senza nome vocabolario - sfrutta macro presente nel template RICERCA_COMMON_MACROS -->
326 <#if hierarchicalCategoriesOptions?? && hierarchicalCategoriesOptions?has_content>
327 <span class="d-flex align-items-center justify-content-between text-uppercase" data-toggle="collapse" data-target="#list-filters" role="button" aria-expanded="true" aria-controls="list-filters">
328 <span>${label_categorie}
329 <#if searchInput.categoryIds?has_content>
330 <span class="badge">${searchInput.categoryIds?size}</span>
331 </#if>
332 </span>
333 </span>
334 <div id="list-filters" class="list-filters collapse show mt-4">
335 <#-- ciclo sulle categorie -->
336 <@getAllCategoriesDesktop hierarchicalCategoriesOptions />
337 </div>
338 </#if>
339 </#if>
340 <#else>
341 <#-- caso di categorie selezionate manualmente dalla select di configurazione -->
342 <#-- caso di elenco di categorie e sottocategorie indentate con nome vocabolario - sfrutta macro presente nel template RICERCA_COMMON_MACROS -->
343 <#if ricercaPortletConfig.configAllowVocabulariesLabelFilter == true>
344 <span class="d-flex align-items-center justify-content-between text-uppercase" data-toggle="collapse" data-target="#list-filters" role="button" aria-expanded="true" aria-controls="list-filters">
345 <span>${label_categorie}
346 <#if searchInput.categoryIds?has_content>
347 <span class="badge">${searchInput.categoryIds?size}</span>
348 </#if>
349 </span>
350 </span>
351 <div id="list-filters" class="list-filters collapse show mt-4">
352 <ul class="list-unstyled">
353 <#list flatVocabulariesAndCategoriesAsKeyValueForMultiSelect as voKey, voValue>
354 <#if voKey == DESTINAZIONI_VOCABULARY_ID>
355 <#-- nn lo visualizzo -->
356 <#else>
357 <li class="mb-2">
358 <a class="accordion-title h4" href="#accordion-content_${voKey}" data-toggle="collapse" aria-expanded="<#if !vocabulariesCollapsed>true<#else>false</#if>">
359 ${voValue.value.label}
360 </a>
361 <div id="accordion-content_${voKey}" class="accordion-content collapse <#if !vocabulariesCollapsed>show</#if>">
362 <@getAllCategoriesDesktop voValue.children />
363 </div>
364 </li>
365 </#if>
366 </#list>
367 </ul>
368 </div>
369 <#else>
370 <#-- caso di elenco categorie e sottocategorie indentate ma senza nome vocabolario - sfrutta macro presente nel template RICERCA_COMMON_MACROS -->
371 <#if flatCategoriesAsKeyValueForMultiSelect?? && flatCategoriesAsKeyValueForMultiSelect?has_content>
372 <span class="d-flex align-items-center justify-content-between text-uppercase" data-toggle="collapse" data-target="#list-filters" role="button" aria-expanded="true" aria-controls="list-filters">
373 <span>${label_categorie}
374 <#if searchInput.categoryIds?has_content>
375 <span class="badge">${searchInput.categoryIds?size}</span>
376 </#if>
377 </span>
378 </span>
379 <div id="list-filters" class="list-filters collapse show mt-4">
380 <@getAllCategoriesDesktop flatCategoriesAsKeyValueForMultiSelect />
381 </div>
382 </#if>
383 </#if>
384 </#if>
385 <#else>
386 <#-- visualizzo una select singola -->
387 <label for="select-ricerca-categorie" class="d-block text-uppercase mb-4">${label_categorie}</label>
388 <select name="categoryIds" class="form-control" id="select-ricerca-categorie">
389 <option value="">${label_seleziona}</option>
390 <#list categoriesAsKeyValue?keys as prop>
391 <#assign isSelected = "" />
392 <#if searchInput.categoryIds?has_content>
393 <#list searchInput.categoryIds as categoryId>
394 <#if categoryId?string == prop>
395 <#assign isSelected = "selected" />
396 </#if>
397 </#list>
398 </#if>
399 <option value="${prop}" ${isSelected}>${categoriesAsKeyValue[prop]}</option>
400 </#list>
401 </select>
402 </#if>
403 </div>
404 </div>
405 </#if>
406
407 <#-- lista risultati -->
408 <#-- modificato da col-md-9 a col-lg-9 -->
409 <#if (ricercaPortletConfig.configSearchByCategories == true) && (ricercaPortletConfig.configShowCategoriesFilterInSidebar == true) && (categoriesAsKeyValue?size > 0)>
410 <div class="col-12 col-lg-9 p-0 search-list-results-container">
411 <#else>
412 <div class="col-12 p-0 search-list-results-container">
413 </#if>
414 <#-- numero risultati + range di ricerca corrente + pulsante cambio data -->
415 <#-- aggiunta classe m-0 -->
416 <#if (ricercaPortletConfig.configShowResultNumber == true) ||
417 (ricercaPortletConfig.configShowResultTitle == true && ricercaPortletConfig.configResultTitleLabel != "")||
418 (ricercaPortletConfig.configCanExportSearchResults == true)>
419 <div class="row search-list-results-setting m-0">
420 <div class="col-10 col-sm-10 col-md-10 col-lg-10 col-xl-8 d-flex align-items-center">
421 <#if (ricercaPortletConfig.configShowResultNumber == true) || (ricercaPortletConfig.configShowResultTitle == true && ricercaPortletConfig.configResultTitleLabel != "")>
422 <div class="d-inline-block mb-0 position-relative h3 search-list-results-number no-arrow">
423 <#if ricercaPortletConfig.configShowResultNumber == true>${curEntry.paging.totalRecordsCount} </#if><#if ricercaPortletConfig.configShowResultTitle == true && ricercaPortletConfig.configResultTitleLabel != "">${ricercaPortletConfig.configResultTitleLabel}</#if>
424 </div>
425 </#if>
426 </div>
427
428 <#if ricercaPortletConfig.configCanExportSearchResults == true>
429 <div class="col-2 col-sm-2 col-md-2 col-lg-2 col-xl-4">
430 <button onclick="downloadCsv('${ricercaPortletConfig.configExportCsvUrl}')" class="btn btn-secondary btn-sm float-right" title="${label_scarica_formato_csv}"><span class="fa-solid fa-download" aria-hidden="true"></span></button>
431 </div>
432 </#if>
433 </div>
434 </#if>
435
436 <#if curEntry.records?has_content>
437 <#-- GRIGLIA: solo questa modalita, per cui non vengono utilizzati i vari controlli per lo switch -->
438 <#-- contenitori vista griglia -->
439 <div class="search-list-results grid-view">
440 <div class="grid-view-container">
441 <#-- aggiunta classe m-0 -->
442 <div class="row row-cols-2 row-cols-sm-2 row-cols-md-2 row-cols-lg-3 list-results m-0">
443
444 <#-- ciclo sugli elementi restituiti dalla ricerca -->
445 <#list curEntry.records as record>
446 <#if record.metadata?has_content>
447
448 <#-- assegno alla variabile il titolo di liferay o di struttura, ripulito dai caratteri accentati, da usare come alt o title -->
449 <#assign titoloScheda = "" />
450 <#if record.title?has_content>
451 <#assign titoloScheda = htmlUtil.escapeAttribute(record.title) />
452 </#if>
453 <#if record.metadata.destinationDestinationName?has_content && record.metadata.destinationDestinationName.value?has_content>
454 <#assign titoloScheda = htmlUtil.escapeAttribute(record.metadata.destinationDestinationName.value) />
455 </#if>
456
457 <#-- singolo elemento -->
458 <div class="col position-relative">
459 <#-- funzione aggiungi a trip planner: visualizzo il cuore solo se esistono le coordinate -->
460 <#-- creo variabile di appoggio, poiche devo eventualmente anche eliminare l'angolo bianco della card che sta sotto -->
461 <#-- al contrario dei template, qui i valori delle coordinate sono numeri e non stringhe -->
462 <#assign existLatLong = false />
463 <#if (record.latitude?has_content && record.latitude != 0) && (record.longitude?has_content && record.longitude != 0)>
464 <#assign existLatLong = true />
465 <span class="fa-regular fa-heart transition" title="${label_aggiungi_lista_viaggio}" data-id="idPoi_${record.entryClassPk}"></span>
466 </#if>
467
468 <div class="result-container">
469 <#-- aggiunta classe m-0 -->
470 <div class="row d-flex justify-content-center align-items-center position-relative card-container <#if !existLatLong>no-fa-heart</#if> transition m-0">
471
472 <#-- richiamo l'immagine: fa parte di un fieldset ripetibile, e potrebbe essere una sequenza o una semplice stringa -->
473 <#-- creo una variabile di appoggio per l'url dell'img da usare nella mappa -->
474 <#assign mapImageUrlJs = "" />
475 <#if record.metadata.destinationImage?has_content && record.metadata.destinationImage.value?has_content>
476 <#if record.metadata.destinationImage.value?is_sequence>
477 <#assign numImg = 0 />
478 <#list record.metadata.destinationImage.value as curImage>
479 <#if curImage?? && curImage?has_content>
480 <#attempt>
481 <#assign curImageJson = jsonFactoryUtil.createJSONObject(curImage) />
482 <#if curImageJson?? && curImageJson?has_content && curImageJson.uuid?? && curImageJson.groupId??>
483 <#assign numImg = numImg + 1 />
484 <#assign curImageDLFileEntry = dlFileEntryLocalService.getFileEntryByUuidAndGroupId(curImageJson.uuid,curImageJson.groupId?number) />
485 <#assign curImageUrl = "/documents/"+groupId+"/"+curImageDLFileEntry.getFolderId()+"/"+curImageDLFileEntry.getFileName()+"/"+curImageJson.uuid />
486 <#assign mapImageUrlJs = curImageUrl />
487
488 <div class="col-12 p-0 m-0">
489 <div class="position-relative image-container transition">
490 <img src="${curImageUrl}" class="image-preview-object-fit" alt="${titoloScheda}">
491 </div>
492 </div>
493
494 </#if>
495 <#recover>
496 <#-- in caso di problemi sull'immagine, non visualizzo nulla -->
497 </#attempt>
498 </#if>
499 <#if numImg == 1>
500 <#break>
501 </#if>
502 </#list>
503 <#else>
504 <#attempt>
505 <#assign imageJson = jsonFactoryUtil.createJSONObject(record.metadata.destinationImage.value) />
506 <#if imageJson?? && imageJson?has_content && imageJson.uuid?? && imageJson.groupId??>
507 <#assign imageDLFileEntry = dlFileEntryLocalService.getFileEntryByUuidAndGroupId(imageJson.uuid,imageJson.groupId?number) />
508 <#assign imageUrl = "/documents/"+groupId+"/"+imageDLFileEntry.getFolderId()+"/"+imageDLFileEntry.getFileName()+"/"+imageJson.uuid />
509 <#assign mapImageUrlJs = imageUrl />
510
511 <div class="col-12 p-0 m-0">
512 <div class="position-relative image-container transition">
513 <img src="${imageUrl}" class="image-preview-object-fit" alt="${titoloScheda}">
514 </div>
515 </div>
516 </#if>
517 <#recover>
518 <#-- in caso di problemi sull'immagine, non visualizzo nulla -->
519 </#attempt>
520 </#if>
521 </#if>
522
523 <#-- testi -->
524 <div class="col-md-12 p-3">
525 <div class="text-center text-container">
526
527 <#-- titolo di struttura -->
528 <#if record.metadata.destinationDestinationName?has_content && record.metadata.destinationDestinationName.value?has_content>
529 <#if record.detailUrl?has_content>
530 <a href="${record.detailUrl}" title="${label_vai_dettaglio} ${htmlUtil.escapeAttribute(record.metadata.destinationDestinationName.value)}">
531 <#else>
532 <a href="javascript:void(0);" title="${label_vai_dettaglio} ${htmlUtil.escapeAttribute(record.metadata.destinationDestinationName.value)}">
533 </#if>
534 <div class="transition tit-link check-highlight">
535 <span class="fa fa-map-marker" aria-hidden="true"></span> ${record.metadata.destinationDestinationName.value?trim}
536 </div>
537 </a>
538 </#if>
539
540 <#-- luogo -->
541 <#assign mapAddressJs = "" />
542 <#if
543 (record.metadata.destinationAddressStreetName?has_content && record.metadata.destinationAddressStreetName.value?has_content) ||
544 (record.metadata.destinationAddressCity?has_content && record.metadata.destinationAddressCity.value?has_content) ||
545 (record.metadata.destinationAddressZipCode?has_content && record.metadata.destinationAddressZipCode.value?has_content) ||
546 (record.metadata.destinationAddressProvince?has_content && record.metadata.destinationAddressProvince.value?has_content) ||
547 (record.metadata.destinationAddressCountry?has_content && record.metadata.destinationAddressCountry.value?has_content)>
548 <div class="mb-3 search-extra-field-place">
549 <strong class="text-uppercase">
550 <#if record.metadata.destinationAddressStreetName?has_content && record.metadata.destinationAddressStreetName.value?has_content>
551 <#assign mapAddressJs = mapAddressJs + record.metadata.destinationAddressStreetName.value + " " />
552 <span>${record.metadata.destinationAddressStreetName.value} </span>
553
554 <#if record.metadata.destinationAddressStreetNumber?has_content && record.metadata.destinationAddressStreetNumber.value?has_content>
555 <#assign mapAddressJs = mapAddressJs + record.metadata.destinationAddressStreetNumber.value + ", " />
556 <span>${record.metadata.destinationAddressStreetNumber.value}, </span>
557 </#if>
558 </#if>
559 <#if record.metadata.destinationAddressCity?has_content && record.metadata.destinationAddressCity.value?has_content>
560 <#assign mapAddressJs = mapAddressJs + record.metadata.destinationAddressCity.value + " "/>
561 <span>${record.metadata.destinationAddressCity.value} </span>
562 </#if>
563 <#if record.metadata.destinationAddressZipCode?has_content && record.metadata.destinationAddressZipCode.value?has_content>
564 <#assign mapAddressJs = mapAddressJs + "- "+ record.metadata.destinationAddressZipCode.value + " " />
565 <span>- ${record.metadata.destinationAddressZipCode.value} </span>
566 </#if>
567 <#if record.metadata.destinationAddressProvince?has_content && record.metadata.destinationAddressProvince.value?has_content>
568 <#assign mapAddressJs = mapAddressJs + "("+record.metadata.destinationAddressProvince.value+") " />
569 <span>(${record.metadata.destinationAddressProvince.value}) </span>
570 </#if>
571 <#if record.metadata.destinationAddressCountry?has_content && record.metadata.destinationAddressCountry.value?has_content>
572 <#assign mapAddressJs = mapAddressJs + record.metadata.destinationAddressCountry.value />
573 <span class="text-uppercase">${record.metadata.destinationAddressCountry.value}</span>
574 </#if>
575 </strong>
576 </div>
577 </#if>
578
579 <#-- sito web + telefono + email -->
580 <#assign mapInfoJs = "" />
581 <#if (record.metadata.destinationWebsite?has_content && record.metadata.destinationWebsite.value?has_content) ||
582 (record.metadata.destinationPhoneNumber?has_content && record.metadata.destinationPhoneNumber.value?has_content && record.metadata.destinationPhoneNumber.value != "0039") ||
583 (record.metadata.destinationEmail?? && record.metadata.destinationEmail.value?has_content)>
584 <div class="transition link check-highlight">
585 <#-- sito web -->
586 <#if record.metadata.destinationWebsite?has_content && record.metadata.destinationWebsite.value?has_content>
587 <#assign mapInfoJs += "<a href='${record.metadata.destinationWebsite.value?ensure_starts_with('[a-zA-Z]+://', 'http://')}' target='_blank' class='link sito d-inline-block mx-1' title='${label_vai_sito_web}: ${record.metadata.destinationWebsite.value}'><span class='fa-solid fa-earth' aria-hidden='true'></span> ${label_sito_web}</a>" />
588 <a href="${record.metadata.destinationWebsite.value?ensure_starts_with('[a-zA-Z]+://', 'http://')}" target="_blank" class="link sito d-inline-block" title="${label_vai_sito_web}: ${record.metadata.destinationWebsite.value}">
589 <span class="fa-solid fa-earth" aria-hidden="true"></span>
590 ${label_sito_web}
591 </a>
592 </#if>
593 <#-- telefono -->
594 <#if record.metadata.destinationPhoneNumber?has_content && record.metadata.destinationPhoneNumber.value?has_content && record.metadata.destinationPhoneNumber.value != "0039">
595 <#assign mapInfoJs += "<a href='tel:${record.metadata.destinationPhoneNumber.value}' class='link tel d-inline-block mx-1' title='${label_chiama_telefono}: ${record.metadata.destinationPhoneNumber.value}'><span class='fa-solid fa-phone' aria-hidden='true'></span> ${record.metadata.destinationPhoneNumber.value}</a>" />
596 <a href="tel:${record.metadata.destinationPhoneNumber.value}" class="link tel d-inline-block" title="${label_chiama_telefono}: ${record.metadata.destinationPhoneNumber.value}">
597 <span class="fa-solid fa-phone" aria-hidden="true"></span> ${record.metadata.destinationPhoneNumber.value}
598 </a>
599 </#if>
600 <#-- email -->
601 <#if record.metadata.destinationEmail?? && record.metadata.destinationEmail.value?has_content>
602 <#assign mapInfoJs += "<a href='mailto:${record.metadata.destinationEmail.value}' class='link sito d-inline-block mx-1' title='${label_invia_email_a}: ${record.metadata.destinationEmail.value}'><span class='fa-solid fa-envelope' aria-hidden='true'></span> ${label_e_mail}</a>" />
603 <a href="mailto:${record.metadata.destinationEmail.value}" class="link sito d-inline-block" title="${label_invia_email_a}: ${record.metadata.destinationEmail.value}">
604 <span class="fa-solid fa-envelope" aria-hidden="true"></span> ${label_e_mail}
605 </a>
606 </#if>
607
608 </div>
609 </#if>
610
611 <#-- leggi tutto -->
612 <#if record.detailUrl?has_content>
613 <a href="${record.detailUrl}" title="${label_vai_dettaglio} ${titoloScheda}">
614 <#else>
615 <a href="javascript:void(0);" title="${label_vai_dettaglio} ${titoloScheda}">
616 </#if>
617 <div class="sub-link">
618 ${label_leggi_tutto} <span class="fa fa-angle-right transition" aria-hidden="true"></span>
619 </div>
620 </a>
621
622 <#-- ******************************* CATEGORIE - COMMENTATO ******************************* -->
623 <#-- QUESTO CODICE È COMMENTATO, POICHÈ LE CATEGORIE NN SONO PREVISTE NEI MOCKUP, MA POTREBBE ESSERE SEMPRE UTILE -->
624
625 <#-- metodo tramite record (ricerca) -->
626 <#-- recupero l'elenco delle categorie dall'oggetto record anzichè passando dall'articolo -->
627 <#--
628 <#if record.categories?? && (record.categories?size > 0)>
629 <#list record.categories as key, value>
630 <#if (key?? && key != 0) && (value?? && value != "")>
631 <div class="chip chip-simple chip-outline-secondary chip-no-link">
632 <span class="chip-label">${value}</span>
633 </div>
634 </#if>
635 </#list>
636 </#if>
637 -->
638
639 <#-- categorie: metodo tramite api -->
640 <#--
641 <#assign categorie = []>
642 <#assign categorie = getListCategoryByArticlePrimKey(record.entryClassPk) />
643 <#if categorie?? && (categorie?size > 0)>
644 <#list categorie as cur_cat>
645 <#if validator.isNotNull(cur_cat) && cur_cat != "">
646 <div class="chip chip-simple chip-outline-secondary chip-no-link">
647 <span class="chip-label">${cur_cat.getTitle(locale)}</span>
648 </div>
649 </#if>
650 </#list>
651 </#if>
652 -->
653 <#-- ******************************* CATEGORIE - COMMENTATO ******************************* -->
654
655 </div>
656 </div>
657 <#-- fine testi -->
658
659 </div>
660 </div>
661 </div>
662 <#-- fine singolo elemento -->
663
664 <#-- valorizzo il marker sulla mappa, se esiste latitudine e longitudine -->
665 <#if (record.latitude?has_content && record.latitude != 0) && (record.longitude?has_content && record.longitude != 0)>
666 <script>
667 var marker = L.marker(L.latLng([${record.latitude},${record.longitude}]));
668
669 var infoBox = "";
670 // apertura contenitore infobox e card
671 infoBox += "<div class='info-box'><div class='card bg-white m-0 border-0 shadow-none'>";
672 // immagine
673 <#if mapImageUrlJs?? && mapImageUrlJs != "">
674 infoBox += "<div class='card-img-wrapper'><img src='${mapImageUrlJs}' class='card-img-top' alt='${titoloScheda}'></div>";
675 </#if>
676 // inizio body
677 infoBox += "<div class='card-body p-2'>"
678 //titolo
679 infoBox += "<h2 class='card-title mb-2'><a href='${record.detailUrl}' title='${label_vai_dettaglio} ${titoloScheda}'>${titoloScheda}</a></h2>"
680 // indirizzo
681 <#if mapAddressJs?? && mapAddressJs != "">
682 infoBox += "<p class='mt-0 mb-2'><span class='fa fa-map-marker mr-1'></span><strong>${mapAddressJs}</strong></p>"
683 </#if>
684 // info
685 <#if mapInfoJs?? && mapInfoJs != "">
686 infoBox += "<p class='mt-0 mb-2'>${mapInfoJs}</p>"
687 </#if>
688 // fine body
689 infoBox += "</div>"
690 // fine contenitore infobox e card
691 infoBox += "</div></div>"
692
693 var popup = marker.bindPopup(infoBox);
694 TglSearchMap.addMarker(marker);
695 </script>
696 </#if>
697
698 </#if>
699 </#list>
700 <#-- fine ciclo -->
701
702 </div>
703 </div>
704 </div>
705 <#-- FINE GRIGLIA -->
706 </#if>
707 <#-- fine curEntry -->
708
709 <#-- paginazione mediante taglib-->
710 <#-- aggiunta classe m-0 -->
711 <div class="row mb-0">
712 <div class="col-12 text-center">
713 <@ricerca_components["ricerca-pagination"]
714 currentPage=curEntry.paging.page?number
715 totalPages=curEntry.paging.totalPagesCount?number
716 paginationUrl="${portletURL}"></@>
717 </div>
718 </div>
719
720 </div>
721 </div>
722 </div>
723
724 </#list>
725 </#if>
726
727 </div>
728</div>
729
730<script>
731 $(document).ready(function(){
732 // carico la mappa
733 TglSearchMap.loadMap(true);
734
735 // scroll fino a ricerca
736 $('html, body').animate({
737 scrollTop: $(".search-template-attrazioni").offset().top
738 }, 500);
739
740 // quando il container collapsed viene visualizzato, ricostruisco la mappa e applico il fitbounds, oltre che a visualizzare il popover, se non ancora visualizzato
741 $('#map-container-desktop').on('shown.bs.collapse', function () {
742 TglSearchMap.getMap().invalidateSize();
743 TglSearchMap.fitBounds();
744
745 // popover on draw circle function
746 if( $('.leaflet-draw-draw-circle').length === 1){
747 if (sessionStorage.getItem('showedPopoverMap') == undefined) {
748 showTooltipOnMap('.search-template-attrazioni .leaflet-draw-draw-circle');
749 }
750 if(isShowedPopover){
751 hideTooltipOnMap('.search-template-attrazioni .leaflet-draw-draw-circle','body');
752 }
753 }
754 });
755
756 // leggo i parametri dall'url: se esiste showMap ed è uguale a true, apro il collapse che contiene la mappa
757 // succede se arrivo dal template form_ricerca (form fittizia)
758 let searchParams = new URLSearchParams(window.location.search)
759 if (searchParams.has('showMap')){
760 let searchParamShowMap = searchParams.get('showMap');
761 if (searchParamShowMap == 'true'){
762 $('#map-container-desktop').collapse('show');
763 }
764 }
765
766 <#-- js per filtri categorie -->
767 <#if (ricercaPortletConfig.configSearchByCategories == true) && (categoriesAsKeyValue?size > 0)>
768 <#-- filtri categorie multiple -->
769 <#if ricercaPortletConfig.configSelectMultipleCategories == true>
770 /*bind checkbox category_*/
771 $('.search-template-attrazioni input[id^="category_"]').click(function(){
772 var $currentElem = $( this );
773 if(!$currentElem.prop('checked')){
774 $(".search-template-attrazioni #search-attrazioni-form-desktop #form_"+$currentElem.attr('id')).remove();
775 }
776 else{
777 $(".search-template-attrazioni #search-attrazioni-form-desktop #form_"+$currentElem.attr('id')).remove();
778 var input = '<input class="mr-2 hide" style="display:none" type="checkbox" name="'+$currentElem.attr('name')+'" id="form_'+$currentElem.attr('id')+'" value="'+$currentElem.val()+'" checked="">';
779 $('.search-template-attrazioni #search-attrazioni-form-desktop .search-form').append(input);
780 }
781 $('.search-template-attrazioni #search-attrazioni-form-desktop').submit();
782 });
783 /*initialize checkbox category_*/
784 $('.search-template-attrazioni input[id^="category_"]').each(function(){
785 var $currentElem = $( this );
786 if($currentElem.prop('checked')){
787 $(".search-template-attrazioni #search-attrazioni-form-desktop #form_"+$currentElem.attr('id')).remove();
788 var input = '<input class="mr-2 hide" style="display:none" type="checkbox" name="'+$currentElem.attr('name')+'" id="form_'+$currentElem.attr('id')+'" value="'+$currentElem.val()+'" checked="">';
789 $('.search-template-attrazioni #search-attrazioni-form-desktop .search-form').append(input);
790 }
791 });
792
793 <#else>
794
795 <#-- filtri categorie singola -->
796 /*bind checkbox category_*/
797 $('.search-template-attrazioni #select-ricerca-categorie').on('change', function() {
798 var $currentElem = $(this);
799 var optionVal = $currentElem.val();
800
801 if(optionVal===undefined || optionVal==null || optionVal==''){
802 $(".search-template-attrazioni #search-attrazioni-form-desktop input[id^='form_']").remove();
803 }
804 else{
805 if ($('.search-template-attrazioni #select-ricerca-categorie option[value=optionVal]').is(':selected')){
806 $(".search-template-attrazioni #search-attrazioni-form-desktop input[id^='form_']").remove();
807 }else{
808 $(".search-template-attrazioni #search-attrazioni-form-desktop input[id^='form_']").remove();
809 var input = '<input class="mr-2 hide" style="display:none;" type="checkbox" name="'+$currentElem.attr('name')+'" id="form_'+$currentElem.attr('id')+'" value="'+optionVal+'" checked="">';
810 $('.search-template-attrazioni #search-attrazioni-form-desktop .search-form').append(input);
811 }
812 }
813 $('.search-template-attrazioni #search-attrazioni-form-desktop').submit();
814 });
815 /*initialize checkbox category_*/
816 $('.search-template-attrazioni #select-ricerca-categorie option').each(function(){
817 var $currentElem = $( this );
818 var optionVal = $currentElem.val();
819 if(optionVal!==undefined && optionVal!=null && optionVal!=''){
820 if($currentElem.is(':selected')){
821 $(".search-template-attrazioni #search-attrazioni-form-desktop input[id^='form_']").remove();
822 var input = '<input class="mr-2 hide" style="display:none;" type="checkbox" name="'+$currentElem.attr('name')+'" id="form_'+$currentElem.attr('id')+'" value="'+$currentElem.val()+'" checked="">';
823 $('.search-template-attrazioni #search-attrazioni-form-desktop .search-form').append(input);
824 }
825 }
826 });
827 </#if>
828 </#if>
829
830 });
831</script>
Esplora come un vero local
Ecco alcuni itinerari per scoprire le Marche