1<#assign resourcePrimaryKey = .vars["reserved-article-resource-prim-key"].data />
2<#assign idArticle = .vars["reserved-article-id"].data />
3
4<#-- assetEntry-->
5<#assign assetEntryLocalService = serviceLocator.findService("com.liferay.asset.kernel.service.AssetEntryLocalService") />
6<#assign assetTagLocalService = serviceLocator.findService("com.liferay.asset.kernel.service.AssetTagLocalService") />
7<#assign assetCategoryLocalService = serviceLocator.findService("com.liferay.asset.kernel.service.AssetCategoryLocalService") />
8<#assign assetVocabularyLocalService= serviceLocator.findService("com.liferay.asset.kernel.service.AssetVocabularyLocalService") />
9<#assign assetEntry = assetEntryLocalService.fetchEntry("com.liferay.journal.model.JournalArticle", resourcePrimaryKey?number) />
10<#if !assetEntry?has_content>
11 <#stop "Error: Asset Entry with resourcePrimaryKey = ${resourcePrimaryKey} not found. Template execution stopped." />
12</#if>
13
14<#-- JournalArticle-->
15<#assign journalArticleLocalService = serviceLocator.findService("com.liferay.journal.service.JournalArticleLocalService") />
16<#assign journalArticle = journalArticleLocalService.fetchLatestArticle(resourcePrimaryKey?number) />
17<#if !journalArticle?has_content>
18 <#stop "Error: Journal Article with resourcePrimaryKey = ${resourcePrimaryKey} not found. Template execution stopped." />
19</#if>
20
21<div class="thematic-area-page-content mt-5">
22 <div class="page-intro">
23
24 <!-- Publish Date -->
25 <#if assetEntry.getPublishDate()?has_content>
26 <div class="subtitle-div gap-3">
27 <h4 class="subtitle plain-text mb-0"> ${dateUtil.getDate(assetEntry.getPublishDate(), "d MMMM yyyy", locale, timeZone)?upper_case}</h4>
28 </div>
29 </#if>
30
31 <!-- Title -->
32 <h1 class="title">${journalArticle.getTitle(themeDisplay.getLocale())}</h1>
33
34 <!-- Subtitle -->
35 <div class="subtitle-div flex-wrap">
36 <h4 class="subtitle plain-text col-md-6 p-0">${languageUtil.get(locale, 'authors')}: ${authors.getData()}</h4>
37 <#assign dateObj = dateUtil.parseDate("yyyy-MM-dd", publishDateCustom.getData(), locale) />
38 <h4 class="subtitle plain-text col-md-6 col-12 p-0">${languageUtil.get(locale, 'document-language')}: ${documentLanguage.getData()!""}</h4>
39 <h4 class="subtitle plain-text col-md-6 col-12 p-0">${languageUtil.get(locale, 'publish-date')}: ${dateUtil.getDate(dateObj, "d MMMM yyyy", locale, timeZone)}</h4>
40 <h4 class="subtitle plain-text col-md-6 col-12 p-0">${languageUtil.get(locale, 'citation')}: ${(citation.getData())!""}</h4>
41 <h4 class="subtitle plain-text col-md-6 col-12 p-0">DOI: ${(doi.getData())!""}</h4>
42 <h4 class="subtitle plain-text col-md-6 col-12 p-0">${languageUtil.get(locale, 'keywords-vivilibrary')}: ${(keywords.getData())!""}</h4>
43 </div>
44
45 <#assign distributionLevelChoice = distributionLevel.getData()!""/>
46 <div class="distribution-level mt-3 ${distributionLevelChoice}">
47 <b>${languageUtil.get(locale, distributionLevelChoice)}</b>
48 </div>
49
50 <!-- Main Image -->
51 <#assign mainImageUrl = ""/>
52 <#assign mainImageCss = ""/>
53 <#if uploadDocument.document.getData()?has_content>
54 <#assign mainImageUrl = uploadDocument.document.getData() + "&previewFileIndex=1">
55 <#assign mainImageCss = "main-image-crop"/>
56 <#elseif journalArticle.getArticleImageURL(themeDisplay)?has_content>
57 <#assign mainImageUrl = journalArticle.getArticleImageURL(themeDisplay)>
58 </#if>
59 <#if mainImageUrl?has_content>
60 <div class="main-image-div">
61 <img class="main-image ${(mainImageCss)!""}" src="${mainImageUrl}" alt="scientific article">
62 </div>
63 </#if>
64
65 <#-- Categories -->
66 <#assign vocabularyName = "Therapy" />
67 <#assign vocabularyId = 0 />
68 <#assign vocabulary = assetVocabularyLocalService.fetchGroupVocabulary(themeDisplay.getScopeGroupId(), vocabularyName)/>
69 <#if vocabulary??>
70 <#assign vocabularyId = vocabulary.getVocabularyId()/>
71 </#if>
72
73 <#assign cats = [] />
74 <#if assetEntry.getCategories()?has_content>
75 <#list assetEntry.getCategories() as c>
76 <#if c.getVocabularyId() == vocabularyId>
77 <#assign cats = cats + [c] />
78 </#if>
79 </#list>
80 </#if>
81
82 <#assign tags = [] />
83 <#if assetTagLocalService.getAssetEntryAssetTags(assetEntry.getEntryId())?has_content>
84 <#assign tags = assetTagLocalService.getAssetEntryAssetTags(assetEntry.getEntryId())>
85 </#if>
86
87 <#if tags?has_content || cats?has_content>
88 <div class="tags-container flex-wrap">
89 <#if cats?has_content>
90 <#list cats as cat>
91 <span class="tag tag-style">${languageUtil.get(locale, "account-treatment-"+cat.getTitle(themeDisplay.getLocale()))}</span>
92 </#list>
93 </#if>
94 <#if tags?has_content>
95 <#list tags as tag>
96 <span class="tag tag-style">${tag.getName()}</span>
97 </#list>
98 </#if>
99 </div>
100 </#if>
101 </div>
102
103 <!-- Wavy divider -->
104 <div class="wavy-divider">
105 <svg viewBox="0 0 1440 60" fill="none" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="none">
106 <path d="M0,20 Q360,60 720,20 T1440,20 V60 H0 Z" fill="#fff"/>
107 </svg>
108 </div>
109
110 <!-- 1. One column section -->
111 <#if shortAbstract.oneColumnSectionTitle.getData()?has_content && shortAbstract.oneColumnText.getData()?has_content>
112 <div class="sections-block">
113 <h4 class="section-title reading-time-parameter">${shortAbstract.oneColumnSectionTitle.getData()}</h4>
114 <div class="one-column-text p-0">
115 <div class="plain-text reading-time-parameter">${shortAbstract.oneColumnText.getData()}</div>
116 </div>
117 </div>
118 </#if>
119
120 <!-- 3. Two columns section -->
121 <#if abstract.title.getData()?has_content &&
122 abstract.text.getData()?has_content>
123 <div class="sections-block">
124 <h4 class="section-title reading-time-parameter">${abstract.title.getData()}</h4>
125 <div class="col-md-12 p-0">
126 <div class="plain-text reading-time-parameter multi-column-text">${abstract.text.getData()}</div>
127 </div>
128 </div>
129 </#if>
130
131 <!-- 5. Embedded document section -->
132 <#if uploadDocument.docSectionTitle.getData()?has_content && uploadDocument.document.getData()?has_content && uploadDocument.document.getData()?has_content>
133 <#assign totalPages = webContentUtil.getDocumentPreviewPageCount(themeDisplay, uploadDocument.document.getData()) />
134 <div class="sections-block">
135 <h4 class="section-title reading-time-parameter">${uploadDocument.docSectionTitle.getData()}</h4>
136 <div class="doc-container">
137 <div class="col-md-12 p-0">
138 <div class="doc-embed d-flex flex-column align-items-center">
139 <button class="pdf-open-doc" data-pdf-url="${uploadDocument.document.getData()}">
140 ${languageUtil.get(locale, "open-document")}
141 </button>
142 <div class="pdf-pages-container">
143 <#list 1..totalPages as page>
144 <img
145 src="${uploadDocument.document.getData()}&previewFileIndex=${page}"
146 class="pdf-page"
147 data-page="${page}"
148 />
149 </#list>
150 </div>
151 <div class="doc-index">
152 <div class="pdf-pagination">
153 <button class="pdf-nav prev subtitle"><</button>
154 <div class="pdf-nav-index-div"></div>
155 <button class="pdf-nav next subtitle">></button>
156 </div>
157 </div>
158 </div>
159 </div>
160 </div>
161 </div>
162 </#if>
163
164 <#assign linkTagIsNotEmpty = false>
165 <#list references.getSiblings() as field>
166 <#if field.getData()?? && field.linkTitle.getData()?trim != "" && field.linkUrl.getData()?trim != "">
167 <#assign linkTagIsNotEmpty = true>
168 <#break>
169 </#if>
170 </#list>
171
172 <!-- 6. Link section -->
173 <#if linkTagIsNotEmpty>
174 <div class="sections-block" style="border-bottom-right-radius:12px;border-bottom-left-radius:12px;">
175 <h4 class="section-title reading-time">${languageUtil.get(locale, "references")}</h4>
176 <div class="d-flex flex-column">
177 <#list references.getSiblings() as link>
178 <#if link.linkTitle.getData()?has_content && link.linkUrl.getData()?has_content>
179 <div class="link-card mb-4">
180 <div class="link-info">
181 <div class="link-title reading-time-parameter"><b>${link.linkTitle.getData()}</b></div>
182 <#if link.linkDesc.getData()?has_content>
183 <div class="link-description reading-time-parameter">${link.linkDesc.getData()}</div>
184 </#if>
185 </div>
186 <a href="${(link.linkUrl.getData())!'javascript:void(0);'}" class="download-btn"
187 title="Scarica" onclick="openLinkInNewTab(event, this)">
188 <i class="icom-vivimedical-link-icon">
189 </i>
190 </a>
191 </div>
192 </#if>
193 </#list>
194 </div>
195 </div>
196 </#if>
197
198</div>
199
200<script src="https://cdn.jsdelivr.net/npm/hammerjs@2.0.8/hammer.min.js"></script>
201
202<script>
203 function initializeOpenDocButton() {
204 const openDocBtn = document.querySelector('.pdf-open-doc');
205 if (openDocBtn) {
206 openDocBtn.addEventListener('click', () => {
207 const pdfUrl = openDocBtn.getAttribute('data-pdf-url');
208 if (!pdfUrl) {
209 alert('PDF not available');
210 return;
211 }
212 const newTab = window.open(pdfUrl, '_blank');
213 if (!newTab) {
214 alert('Unable to open the PDF. Please check your browser settings for pop-ups.');
215 }
216 });
217 }
218 }
219
220 function pdfPagination() {
221 const pages = document.querySelectorAll('.pdf-page');
222 const prevBtn = document.querySelector('.pdf-nav.prev');
223 const nextBtn = document.querySelector('.pdf-nav.next');
224 const pageNumbersContainer = document.querySelector('.pdf-nav-index-div');
225
226 // pdf-pagination struct: 1....lastPage
227 const totalPages = pages.length;
228 const firstPage = 1;
229 const lastPage = totalPages;
230 let currentPage = firstPage;
231 let indexStyleClass = 'subtitle';
232
233 function addStyle(elem, styleClass) {
234 elem.classList.add(styleClass);
235 }
236
237 function addPageButton(pageNumber) {
238 const btn = document.createElement('button');
239 btn.className = 'pdf-page-num';
240 btn.textContent = pageNumber;
241 addStyle(btn, indexStyleClass);
242 pageNumbersContainer.appendChild(btn);
243 }
244
245 function addEllipsis() {
246 const span = document.createElement('span');
247 span.className = 'pdf-page-num ellipsis';
248 span.textContent = '...';
249 addStyle(span, indexStyleClass);
250 pageNumbersContainer.appendChild(span);
251 }
252
253 // invalid currentPage sets value to default (firstPage)
254 function validateCurrentPage(currentPage, defaultValue) {
255 if (typeof currentPage !== 'number' || isNaN(currentPage) ||
256 currentPage < firstPage || currentPage > lastPage) {
257 return defaultValue;
258 }
259 return currentPage;
260 }
261
262 function renderPageNumbers(currentPage) {
263 pageNumbersContainer.innerHTML = '';
264 currentPage = validateCurrentPage(currentPage, firstPage);
265 console.log("totalPages " + totalPages);
266 if (totalPages >= 1 && totalPages <= 4) {
267 for (let i = firstPage; i < firstPage + totalPages; i++) {
268 addPageButton(i);
269 }
270 }
271 else if (currentPage == firstPage || currentPage == firstPage + 1) {
272 addPageButton(firstPage);
273 addPageButton(firstPage + 1);
274 addPageButton(firstPage + 2);
275 addEllipsis();
276 addPageButton(lastPage);
277 } else if (currentPage == lastPage - 1 || currentPage == lastPage) {
278 addPageButton(firstPage);
279 addEllipsis();
280 addPageButton(lastPage - 2);
281 addPageButton(lastPage - 1);
282 addPageButton(lastPage);
283 } else {
284 addPageButton(firstPage);
285 addEllipsis();
286 addPageButton(currentPage);
287 addEllipsis();
288 addPageButton(lastPage);
289 }
290 }
291
292 function setActivePage(currentPage) {
293 if (validateCurrentPage(currentPage, firstPage)) {
294 const buttons = document.querySelectorAll('.pdf-page-num');
295 buttons.forEach(btn => btn.classList.remove('active'));
296 buttons.forEach(btn => {
297 if (parseInt(btn.textContent, 10) === currentPage) {
298 btn.classList.add('active');
299 }
300 });
301 }
302 }
303
304 function showPage(page) {
305 currentPage = page;
306 // hides all images
307 document.querySelectorAll('.pdf-page').forEach(img => img.style.display = 'none');
308 // display currentPage image
309 const currentPageElement = document.querySelector('.pdf-page[data-page="' + page + '"]');
310 if (currentPageElement) {
311 currentPageElement.style.display = 'block';
312 }
313 prevBtn.disabled = currentPage === firstPage;
314 nextBtn.disabled = currentPage === lastPage;
315 renderPageNumbers(currentPage);
316 setActivePage(currentPage);
317 }
318
319 // prev btn listener
320 prevBtn.addEventListener('click', function () {
321 if (currentPage > firstPage) showPage(currentPage - 1);
322 });
323
324 // next btn listener
325 nextBtn.addEventListener('click', function () {
326 if (currentPage < lastPage) showPage(currentPage + 1);
327 });
328
329 // index btn listener
330 pageNumbersContainer.addEventListener('click', function (e) {
331 if (e.target.classList.contains('pdf-page-num') && !e.target.classList.contains('ellipsis')) {
332 const pageNumber = parseInt(e.target.textContent, 10);
333 if (!isNaN(pageNumber)) {
334 showPage(pageNumber);
335 }
336 }
337 });
338
339 //Hammer.js
340 function initializeHammer() {
341 const container = document.querySelector('.pdf-pages-container');
342
343 if (!container || typeof Hammer === 'undefined') {
344 console.warn('Container not found or Hammer.js not loaded');
345 return;
346 }
347
348 const hammer = new Hammer(container);
349
350 hammer.get('swipe').set({ direction: Hammer.DIRECTION_HORIZONTAL });
351
352 hammer.on('swipeleft', () => {
353 if (currentPage < totalPages) {
354 showPage(currentPage + 1);
355 }
356 });
357
358 hammer.on('swiperight', () => {
359 if (currentPage > 1) {
360 showPage(currentPage - 1);
361 }
362 });
363 }
364
365 // Initialize Hammer
366 initializeHammer();
367
368
369 showPage(firstPage);
370 }
371
372 function openLinkInNewTab(event, el) {
373 event.preventDefault();
374 event.stopPropagation();
375
376 const href = el.getAttribute('href');
377
378 if (!href || href.trim() === '' || href === 'javascript:void(0);') {
379 alert('Link non disponibile');
380 return;
381 }
382 try {
383 new URL(href, window.location.href);
384 } catch (e) {
385 alert('URL non valido');
386 return;
387 }
388
389 const newTab = window.open(href, '_blank');
390
391 if (!newTab || newTab.closed || typeof newTab.closed === 'undefined') {
392 alert('Unable to open the link. Please check your browser settings to allow pop-ups.');
393 }
394 }
395
396
397 function init() {
398 pdfPagination();
399 initializeOpenDocButton();
400 }
401 if (document.readyState === "loading") {
402 document.addEventListener('DOMContentLoaded', init);
403 document.addEventListener('DOMContentLoaded', function(){
404 let readingTime = calculateReadingTime();
405 $('#readingTime').text(readingTime);
406 });
407 } else {
408 init();
409 let readingTime = calculateReadingTime();
410 $('#readingTime').text(readingTime);
411 }
412
413 function calculateReadingTime(){
414 let wordCount = 0;
415 const wordsPerMinute = 200;
416 $('.reading-time-parameter').each(function(index, elem){
417 let text = $(elem).text();
418 wordCount += text.trim().split(/\s+/).length;
419 });
420 if(wordCount > 0){
421 return Math.ceil(wordCount / wordsPerMinute);
422 }else{
423 return 0;
424 }
425 }
426
427</script>
428
429<style>
430 .main-image-crop{
431 height: 300px;
432 object-position:top center;
433 @media(max-width: 576px){
434 height: 60px;
435 }
436 }
437 .multi-column-text{
438 column-count: 2;
439 column-gap: 2rem;
440 }
441 .distribution-level{
442 text-transform: uppercase;
443 &:before{
444 content: "";
445 display: inline-block;
446 width: 20px;
447 height: 20px;
448 margin-right: 6px;
449 border: 5px solid;
450 border-radius: 50%;
451 background: transparent;
452 vertical-align: sub;
453 }
454 &.disclosable{
455 &.before{
456 border-color: #23863F;
457 }
458 color: #23863F;
459 }
460 &.limited-disclosure{
461 &.before{
462 border-color: #F7B32B;
463 }
464 color: #F7B32B;
465 }
466 &.internal-use-only{
467 &.before{
468 border-color: #D24445;
469 }
470 color: #D24445;
471 }
472 }
473 .icom-vivimedical-link-icon:before{
474 color: #8ed300;
475 }
476 .thematic-area-page-content{
477 .download-btn:hover{
478 text-decoration: none;
479 }
480 }
481
482 .plain-text{
483 p{
484 line-height: 1.5;
485 }
486 }
487</style>