null Rakas lähiö: Kirjailija Tomi Kontio kasvoi Myllypurossa pojasta mieheksi

Virhe tapahtui prosessoidessa esitysmallia.
The following has evaluated to null or missing:
==> slot.videoFile  [in template "20116#20160#45930" at line 914, column 42]

----
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: #assign videoFile = slot.videoFile.ge...  [in template "20116#20160#45930" in macro "printRemainingVideoContents" at line 914, column 21]
	- Reached through: @printRemainingVideoContents articleV...  [in template "20116#20160#45930" at line 378, column 9]
----
1<#----------------------------------------------------------------------------- 
2    INIT (Single article template) 
3------------------------------------------------------------------------------> 
4<#setting time_zone="Europe/Helsinki"> 
5<#assign serviceContext = staticUtil["com.liferay.portal.kernel.service.ServiceContextThreadLocal"].getServiceContext()> 
6<#assign themeDisplay = serviceContext.getThemeDisplay() /> 
7<#assign javascript_folder = "/o/kirkkojakaupunki-site-theme/js" > 
8 
9 
10<#assign JournalArticleLocalService = serviceLocator.findService("com.liferay.journal.service.JournalArticleLocalService")> 
11<#assign articleContentService = serviceLocator.findService("com.ch5finland.helsinginseurakuntayhtyma.kirkkojakaupunki.article.content.service.ArticleContentService")> 
12 
13<#assign currentArticle = JournalArticleLocalService.getArticle(groupId, .vars['reserved-article-id'].data) /> 
14<#setting url_escaping_charset="UTF-8"> 
15 
16<#-- 
17    Kirkkojakaupunki-site-theme-blank is used pages that are meant for mobile app  
18    Figure out if article is shown on mobile app based on theme id  
19     
20    It is also possible to show article with default kirkkojakaupunki-site-theme when request comes 
21    from mobile app. We can check this with x-prenly-client-type header 
22--> 
23<#assign mobileAppView = themeDisplay.getThemeId()?contains("kirkkojakaupunkisitethemeblank") ||  
24                         request.getHeader('x-prenly-client-type')?? && request.getHeader('x-prenly-client-type')?has_content 
25
26 
27 
28<#-- Helper variables --> 
29<#assign includeOwlScript = false> 
30 
31<#assign previewMode = false > 
32<#-- Check if article is viewed in preview mode --> 
33 
34<#if themeDisplay.getURLCurrent()?contains("preview_article_content.jsp") > 
35    <#-- this aplies if preview is selected via folder/list view. Site css is included  --> 
36    <#assign previewMode = true > 
37</#if> 
38<#if themeDisplay.getURLCurrent()?contains("preview_article_content_template.jsp") > 
39    <#-- this aplies if preview is selected via article edit. Site css is not included for some reason so here is little hack --> 
40    <#assign previewMode = true > 
41    <link rel="stylesheet" type="text/css"  href="/o/kirkkojakaupunki-site-theme/css/main.css" /> 
42    <script>$.getScript("/o/kirkkojakaupunki-site-theme/js/main.js");</script> 
43</#if> 
44 
45<#-- 
46    Counters for keeping track of now many elements 
47    have been placed with a tag. 
48--> 
49<#global tagReplaceCounter = 0 /> 
50<#assign kainaloTagCounter = 0 /> 
51<#assign infoTagCounter = 0 /> 
52<#assign faktaTagCounter = 0 /> 
53<#assign quoteTagCounter = 0 /> 
54<#assign videoTagCounter = 0 /> 
55<#assign audioTagCounter = 0 /> 
56<#assign imageComparisonTagCounter = 0 /> 
57 
58<#-- Article ID --> 
59<#assign articleId = .vars['reserved-article-id'].data> 
60<#assign articleCategories = getCategoryNames(articleId)> 
61 
62 
63<#-- Article --> 
64<#assign article = JournalArticleLocalService.getArticle(groupId, articleId) /> 
65 
66<#-- Article complete URL --> 
67<#assign urlTitle = article.urlTitle?trim /> 
68<#assign articleCompleteUrl = themeDisplay.getPortalURL() + "/-/" + urlTitle /> 
69 
70<#-- available author names : Those authors which have author card --> 
71<#assign authorCardFolderIds = [140767, 140764] /> <#-- Toimittjat, Kolumnistit --> 
72<#assign availableAuthorNames = getAvailableAuthorNames(authorCardFolderIds) /> 
73<#assign authorCardBaseUrl = "/toimittajat?author=" /> 
74 
75<#-- Article title --> 
76<#assign title = .vars['reserved-article-title'].data> 
77 
78<#-- Article lead --> 
79<#assign lead = .vars['reserved-article-description'].data> 
80 
81<#-- Get and format article publish date --> 
82<#assign displayDate = .vars['reserved-article-display-date'].data> 
83 
84    <#-- Save the original page locale for later --> 
85    <#assign originalLocale = locale> 
86 
87    <#-- Set the page locale to the portals default locale --> 
88    <#setting locale = localeUtil.getDefault()> 
89 
90    <#-- Parse the date to a date object --> 
91    <#assign displayDate = displayDate?datetime("EEE, d MMM yyyy HH:mm:ss Z")> 
92 
93    <#-- Set the page locale back to the original page locale --> 
94    <#assign locale = originalLocale> 
95 
96 
97<#-- Article body tags --> 
98<#assign tagKainalo = "[[kainalo]]"> 
99<#assign tagInfo = "[[info]]"> 
100<#assign tagFakta = "[[fakta]]"> 
101<#assign tagQuote = "[[sitaatti]]"> 
102<#assign tagImage = "[[kuva]]"> 
103<#assign tagVideo = "[[video]]"> 
104<#assign tagAudio = "[[audio]]"> 
105<#assign tagCarousel = "[[kuvakaruselli#]]"> <#-- # will be replaced with carousel number --> 
106<#assign tagImageComparison = "[[vaihtokuva]]"> 
107<#assign tagEmbeddedHTML = "[[upotus]]"> 
108 
109<#-- lisäksi käytössä  
110  [[mainos]] 
111  [[eimainosta]] 
112--> 
113 
114<#-- wrapper css classes --> 
115<#assign wrapperCSSKainalo = "article-section article-tail"> 
116<#assign wrapperCSSInfo = "article-box article-info"> 
117<#assign wrapperCSSFakta = "article-box article-fact"> 
118<#assign wrapperCSSQuote = "template-quote"> <#-- should not be changes. It is used in switch --> 
119 
120<#-- Article tags --> 
121<#assign articleTags = .vars['reserved-article-asset-tag-names'].data> 
122 
123<#-- Article raw body --> 
124<#assign articleBody = articleBody.getData()> 
125 
126<#-- Ad :  
127 https://tiketti.ch5.fi/issues/24425 
128  
129    Lähtökohta: Artikkelin sisäinen mainos näkyy aina neljän leipätekstin <p>-tagin jälkeen. 
130    Tarvittaessa artikkelin muokkaaja voi muuttaa mainoksen näyttöpaikkaa [[mainos]]-tagilla 
131    Tarvittaessa artikkelin muokkaaja voi estää mainoksen näkemisen yksittäisessä artikkelissa [[eimainosta]]-tagilla. 
132    Mainos ei näy ”erikoisartikkeleissa” eikä ”kuva-artikkeleissa”, ainoastaan ”Koko artikkeleissa”. 
133    Artikkelin sisäinen mainos ei näy koskaan artikkeleissa, joissa on tägi ”pääkirjoitus” tai ”hyvää pyhää” tai joiden leipäteksti on hyvin lyhyt (pituus alle 8 <p>) 
134    Mainos näytetään kaikissa palvelun vanhoissa artikkeleissa edellä mainituin rajoituksin. 
135    Artikkelin sisäisiä mainospaikkoja voidaan tehdä montakin, mutta ehkä lähdetään yhdellä liikkeelle… 
136    Pyritään testaamaan artikkelin sisäisen mainospaikan toimivuus kehitysympäristössä. 
137 
138     
139    --> 
140<#assign insertAdAfterNthParagraph = 4 /> 
141<#assign insertAdIfContainsMoreThanParagraphs = 8 /> 
142<#assign tagPreventsShowingAd = articleTags?contains("pääkirjoitus") || articleTags?contains("hyvää pyhää") /> 
143 
144<#if !articleBody?contains("[[eimainosta]]") && !tagPreventsShowingAd> 
145 
146    <#if !articleBody?contains("[[mainos]]")> 
147        <#-- no ad tag so inject --> 
148        <#assign articleBody = articleContentService.injectAdMarker(articleBody, insertAdAfterNthParagraph, insertAdIfContainsMoreThanParagraphs) /> 
149    </#if> 
150    <#assign articleBody = replaceAdTag(articleBody) /> 
151<#else> 
152 
153    <#-- Clean [[eimainosta]] and possible [[mainos]] tags -->  
154     
155    <#assign articleBody = articleBody?replace("<p>[[eimainosta]]</p>", "") /> 
156    <#assign articleBody = articleBody?replace("[[eimainosta]]", "") /> 
157    <#assign articleBody = articleBody?replace("<p>[[mainos]]</p>", "") /> 
158    <#assign articleBody = articleBody?replace("[[mainos]]", "") />     
159</#if> 
160 
161<#-- // END OF AD stuff --> 
162 
163<#if articleTail?has_content> 
164    <#assign articleBody = replaceTags(articleBody, tagKainalo, articleTail, wrapperCSSKainalo)> 
165    <#assign kainaloTagCounter = tagReplaceCounter /> 
166</#if> 
167<#if articleInfo?has_content> 
168    <#assign articleBody = replaceTags(articleBody, tagInfo, articleInfo, wrapperCSSInfo)> 
169    <#assign infoTagCounter = tagReplaceCounter /> 
170</#if> 
171<#if articleFacts?has_content> 
172    <#assign articleBody = replaceTags(articleBody, tagFakta, articleFacts, wrapperCSSFakta)> 
173    <#assign faktaTagCounter = tagReplaceCounter /> 
174</#if> 
175<#if articleQuotes?has_content> 
176    <#assign articleBody = replaceTags(articleBody, tagQuote, articleQuotes, wrapperCSSQuote)> 
177    <#assign quoteTagCounter = tagReplaceCounter /> 
178</#if> 
179<#if articleEmbeddedHTML?has_content> 
180    <#assign articleBody = replaceTags(articleBody, tagEmbeddedHTML, articleEmbeddedHTML, 'no-wrapper')> 
181</#if> 
182<#if articleImages?has_content> 
183    <#assign articleBody = replaceImageTags(articleBody, tagImage, articleImages)> 
184    <#assign articleBody = replaceCarouselTags(articleBody, tagCarousel, articleImages)> 
185</#if> 
186<#if articleVideos?has_content> 
187    <#assign articleBody = replaceVideoTags(articleBody, tagVideo, articleVideos)> 
188    <#assign videoTagCounter = tagReplaceCounter /> 
189</#if> 
190<#if articleAudio?has_content> 
191    <#assign articleBody = replaceAudioTags(articleBody, tagAudio, articleAudio)> 
192    <#assign audioTagCounter = tagReplaceCounter /> 
193</#if> 
194<#if imageComparisons?has_content> 
195    <#assign articleBody = replaceImageComparisonTags(articleBody, tagImageComparison, imageComparisons)> 
196</#if> 
197 
198<#-- 25209 - iframe is wrapped inside p-tags and for that reason gets extra text-indent. Replace p-tags. -->  
199<#assign articleBody = articleBody?replace("<p><iframe", "<iframe")?replace("</iframe></p>", "</iframe>") > 
200 
201<#----------------------------------------------------------------------------- 
202    OUTPUT ARTICLE 
203------------------------------------------------------------------------------> 
204 
205<#if previewMode> 
206    <script> 
207            inPreviewMode = true; 
208            var iframeContainer = jQuery('.modal-body-iframe', window.parent.document).parent().parent(); 
209            jQuery(iframeContainer).addClass("custom-preview-dialog"); 
210            jQuery(iframeContainer).css("width", "940px"); 
211            jQuery(iframeContainer).css("margin", "auto"); 
212            jQuery(iframeContainer).find("iframe").css("width", "900px"); 
213            jQuery('body', window.parent.document).addClass("custom-in-preview-mode"); 
214    </script> 
215</#if> 
216 
217<article class="voice-intuitive"> 
218     
219    <#-- Show article url in preview mode --> 
220    <#if previewMode?? && previewMode> 
221	    <div><span>${articleCompleteUrl}</span></div> 
222    </#if> 
223 
224    <#-- Article image(s) --> 
225 
226    <#if articleImages?has_content> 
227        <#assign headerImages = getHeaderImages(articleImages) > 
228        <#assign contentCarouselImages = getContentCarouselImages(articleImages) > 
229    </#if> 
230 
231    <#if contentCarouselImages?has_content> 
232        <#if (contentCarouselImages?size > 1) > 
233            <#assign includeOwlScript = true> 
234        </#if> 
235    </#if> 
236     
237     
238 
239    <#if headerImages?has_content> 
240 
241        <#-- Container with bottom margin --> 
242        <div class="article-header-img"> 
243 
244 
245 
246            <#-- More than one image > create owl-carousel --> 
247            <#if (headerImages?size > 1) > 
248 
249                    <#list headerImages as image> 
250 
251            		    <#if image.getData()?? && image.getData()?has_content> 
252 
253                            <#-- Open carousel container on first image --> 
254                            <#if image?is_first> 
255                                <div class="owl-carousel owl-theme"> 
256 
257                                <#-- Load carousel script at the end of display template --> 
258                                <#assign includeOwlScript = true> 
259                            </#if> 
260 
261                            <#-- Output carousel slides --> 
262                            <div class="item"> 
263                                <#if image.getAttribute("alt")?has_content> 
264                                    <div class="aspect-ratio aspect-ratio-middle aspect-ratio-xs"> 
265                                        <img  class="aspect-ratio-item-fluid" src="${getImageURLByTemplateModel(image, '1200')}" alt="${image.getAttribute("alt")}" /> 
266                                    </div> 
267                                    <span class="caption voice-no-read">${image.getAttribute("alt")}</span> 
268                                <#else> 
269                                    <div class="aspect-ratio aspect-ratio-middle aspect-ratio-xs"> 
270                                        <img class="aspect-ratio-item-fluid" src="${getImageURLByTemplateModel(image, '1200')}" alt="" /> 
271                                    </div> 
272                                </#if> 
273                            </div> 
274 
275                            <#-- Close carousel contaner after last item --> 
276                            <#if image?is_last> 
277                                </div> <#-- div.owl-carousel --> 
278                            </#if> 
279 
280            		    </#if> 
281            	    </#list> 
282 
283            <#else> 
284 
285            <#-- Display a single image without carousel --> 
286            <#list headerImages as image> 
287 
288    		    <#if image.getData()?? && image.getData()?has_content> 
289 
290                    <#-- <@adaptive_media_image["img"] fileVersion=dlAppServiceUtil.getFileEntry(.getFileVersion())/>  --> 
291         
292                    <#if image.getAttribute("alt")?has_content> 
293            	        <img  
294                            src="${getImageURLByTemplateModel(image, '1200')}"  
295                            alt="${image.getAttribute("alt")}" /> 
296                             
297                        <p class="caption voice-no-read">${image.getAttribute("alt")}</p> 
298                    <#else> 
299            	        <img  
300                            src="${getImageURLByTemplateModel(image, '1200')}" alt="" /> 
301                   </#if> 
302    		    </#if> 
303    	    </#list> 
304 
305            </#if> 
306 
307        </div> <#-- div.article-header-img --> 
308 
309    </#if> 
310 
311    <#if articleCategories?has_content> 
312        <div class="voice-no-read"> 
313            <#list articleCategories as cat> 
314                <span data-cat="${cat?lower_case}" class="category-item">${cat}</span> 
315            </#list> 
316        </div> 
317    </#if> 
318 
319    <h1>${title}</h1> 
320 
321    <#if lead?? && lead?has_content> 
322        <#-- Remove possible HTML-tags --> 
323        <#assign leadCleaned = htmlUtil.stripHtml(lead) /> 
324        <p class="lead">${leadCleaned}</p> 
325    </#if> 
326 
327    <ul class="list-inline article-meta voice-no-read""> 
328        <li class="list-inline-item"><span class="date">${displayDate?string["dd.MM.yyyy HH:mm"]}</span></li> 
329 
330        <#if articleAuthors.authors.getData()?? && articleAuthors.authors.getData()?has_content> 
331            <#assign articleAuthor = articleAuthors.authors.getData()> 
332            <li class="list-inline-item"> 
333                <span class="author authortext-no-margin author-text"></span> 
334                <span class="author author-name"> 
335                    <#if mobileAppView> 
336                        ${articleAuthor} 
337                    <#else> 
338                        ${makeAuthorLinks(articleAuthor,availableAuthorNames, authorCardBaseUrl)} 
339                    </#if> 
340                </span> 
341            </li> 
342        </#if> 
343 
344        <#if articleAuthors.photographers.getData()?? && articleAuthors.photographers.getData()?has_content> 
345            <li class="list-inline-item"> 
346                <span class="author author-name author-photo"> 
347                    <#if mobileAppView> 
348                        ${articleAuthors.photographers.getData()} 
349                    <#else> 
350                        ${makeAuthorLinks(articleAuthors.photographers.getData(),availableAuthorNames, authorCardBaseUrl)} 
351                    </#if> 
352                </span> 
353            </li> 
354        </#if> 
355    </ul> 
356 
357    <div id="voice-intuitive-root"></div> 
358     
359    ${articleBody} 
360 
361    <#-- Output article quotes --> 
362    <#if articleQuotes?has_content && articleQuotes.getSiblings()?has_content> 
363    <@printRemainingTagContents articleQuotes quoteTagCounter wrapperCSSQuote /> 
364    </#if> 
365 
366    <#-- Print article factbox(es) --> 
367    <#if articleFacts?has_content && articleFacts.getSiblings()?has_content> 
368        <@printRemainingTagContents articleFacts faktaTagCounter wrapperCSSFakta /> 
369    </#if> 
370 
371    <#-- Print article infobox(es) --> 
372    <#if articleInfo?has_content && articleInfo.getSiblings()?has_content> 
373        <@printRemainingTagContents articleInfo infoTagCounter wrapperCSSInfo/> 
374    </#if> 
375 
376    <#-- Print article video(s) --> 
377    <#if articleVideos?has_content && articleVideos.getSiblings()?has_content> 
378        <@printRemainingVideoContents articleVideos videoTagCounter  /> 
379    </#if> 
380 
381    <#-- Print remaining audio embeds --> 
382    <#if articleAudio?has_content && articleAudio.getSiblings()?has_content> 
383        <@printRemainingAudioContents articleAudio audioTagCounter  /> 
384    </#if> 
385     
386    <#-- Print article tail(s) --> 
387    <#if articleTail?has_content && articleTail.getSiblings()?has_content> 
388        <@printRemainingTagContents articleTail kainaloTagCounter wrapperCSSKainalo /> 
389    </#if> 
390 
391     
392    <div class="voice-no-read">  
393 
394 
395 
396     <#-- R&S sharing buttons --> 
397        <div class="article-sharing"> 
398            <p class="sans text-uppercase">Jaa t&auml;m&auml; artikkeli:</p> 
399            <div class="rns-share-plugin"></div> 
400        </div> 
401         
402<#if !mobileAppView>         
403    <#-- City --> 
404        <#if articleTags?has_content> 
405 
406            <#assign cityTags = 0> 
407 
408            <#list articleTags?split(",") as tag> 
409                <#assign cities = ["Helsinki", "Espoo", "Vantaa", "Kauniainen", "helsinki", "espoo", "vantaa", "kauniainen"]>  
410                <#assign isCityTag = cities?seq_contains(tag)>  
411                <#if isCityTag == true> 
412                    <#assign cityTags = cityTags + 1> 
413                </#if> 
414            </#list> 
415 
416            <#if cityTags gt 0> 
417                <div class="article-section article-tags"> 
418                    <p class="sans text-uppercase d-inline">Lis&auml;&auml; aiheesta:</p> 
419                    <ul class="list-inline d-inline"> 
420                        <#list articleTags?split(",") as tag> 
421 
422                            <#assign cities = ["Helsinki", "Espoo", "Vantaa", "Kauniainen", "helsinki", "espoo", "vantaa", "kauniainen"]>  
423                            <#assign isCityTag = cities?seq_contains(tag)>  
424 
425                            <#if isCityTag> 
426                                <li class="list-inline-item"><a href="/artikkelit/-/tag/${htmlUtil.escapeURL(tag)}?article=${article.getResourcePrimKey()}" class="tag tag-map-icon">${tag}</a></li> 
427                            </#if> 
428                        </#list> 
429                    </ul> 
430                </div> 
431            </#if> 
432        </#if> 
433</#if> <#-- end of !mobileAppView -->         
434         
435        <#-- 
436            Output author card for the main author. 
437        --> 
438 
439        <#-- <#if articleAuthors.mainAuthor.getData()?? && articleAuthors.mainAuthor.getData()?has_content> 
440            <#assign authorCard = articleAuthors.mainAuthor.getData()?eval /> 
441            <@liferay_ui["asset-display"] 
442                className=authorCard.className 
443                classPK=getterUtil.getLong(authorCard.classPK, 0) 
444                template="full_content" 
445            /> 
446        </#if> --> 
447 
448        <#-- react&share buttons --> 
449         
450        <#-- Hide Kiinostavimmat nyt --> 
451        <#if mobileAppView>  
452        <style> 
453            #rns-post-recommender { display: none; } 
454        </style> 
455        </#if> <#-- end of mobileview --> 
456         
457         
458        <div class="rns"></div> 
459         
460        <script type="text/javascript"> 
461        (function() { 
462        'use strict'; 
463        var a=document.querySelector(".article-meta .date"), 
464            b=b?b.innerHTML:"", 
465            d=document.querySelector("article h1"), 
466            e=document.querySelector("a.author-name"); 
467        var rnsRecommend = function() { window.rnsRecommend() }; 
468        window.rnsData={ 
469            recommenderToggle: '.article-assets', 
470            apiKey:"oyrv5xd6dmwj3lkm", 
471            reactionCallback: rnsRecommend, 
472            maxRecommendations: 3, 
473            date:function(f){ 
474                try{ 
475                    var a=f.match(/(\d{1,2})\.(\d{1,2})\.(\d{4})(\s+(\d{1,2}):(\d{2}))*/), 
476            c=new Date(Date.UTC.apply(this,a[4]?[a[3],a[2]-1,a[1],a[5],a[6]]:[a[3],a[2]-1,a[1]]));return c.setHours(c.getHours()-2),c.toISOString()}catch(g){return""}}(b),title:d?d.innerHTML:"",author:e?e.innerHTML:"", 
477        canonicalUrl:"${articleCompleteUrl}"}; 
478        b=document.createElement("script"); 
479        b.src="https://cdn.reactandshare.com/plugin/rns.js";document.body.appendChild(b); 
480        b=document.createElement("script"); 
481        b.src="https://cdn.reactandshare.com/recommender/rnsrw.js"; 
482        document.body.appendChild(b); 
483 
484    })(); 
485        </script> 
486 
487 
488        <#-- MORE FROM AUTHOR --> 
489 
490        <#assign hasAuthor = articleAuthors.authors.getData()?? && articleAuthors.authors.getData()?has_content /> 
491        <#assign hasPhotographer = articleAuthors.photographers.getData()?? && articleAuthors.photographers.getData()?has_content /> 
492         
493        <#if hasAuthor || hasPhotographer> 
494            <#assign articleAuthor = articleAuthors.authors.getData()> 
495            <#assign articlePhotographer = articleAuthors.photographers.getData() /> 
496             
497            <#assign authorObjs = [] /> 
498 
499            <#-- Find which authors (author card titles) appears in author field and make list of those... 
500            We need no maintain order of appearance 
501            --> 
502            <#list availableAuthorNames as authorName> 
503                <#assign authorPos = articleAuthor?index_of(authorName) /> 
504                <#assign photographerPos = articlePhotographer?index_of(authorName) /> 
505                 
506                <#-- Keep the order... first authors then photographers --> 
507                <#if authorPos != -1 || photographerPos != -1> 
508                    <#assign pos = authorPos /> 
509                    <#if authorPos == -1 && photographerPos != -1> 
510                        <#assign pos = 1000 + photographerPos /> 
511                    </#if> 
512                    <#assign authorObj = {"pos" : pos, "name": authorName} /> 
513                    <#assign authorObjs = authorObjs + [authorObj] /> 
514                </#if> 
515            </#list> 
516 
517            <#if authorObjs?size gt 0> 
518                <#assign authorObjs = authorObjs?sort_by("pos") /> 
519 
520                <#-- <div class="article-tags"> 
521                    <p class="sans text-uppercase">Lis&auml;&auml; tekij&auml;lt&auml;:</p> 
522                    <ul class="list-inline"> 
523                        <#list authorObjs as authorObj> 
524                            <li class="list-inline-item"> 
525                                <#assign authorSearchURL = "/toimittajat?author=" /> 
526                                <a class="tag tag-default" href="${authorCardBaseUrl}${authorObj.name?url}">${authorObj.name}</a> 
527                            </li>		   
528                        </#list> 
529                    </ul> 
530                </div> --> 
531            </#if> 
532 
533        </#if> 
534 
535 
536        <#-- RELATED CONTENT  
537             
538            By default related content are ordered by publish date - newest first. 
539             
540            It is possible to prioritized content in article. Article must be added as related content AND  
541            it is selected into priotized content field. Editor can choose multiple prioritized content and then 
542            order is kept as it is organized in  
543         
544        -->  
545         
546        <#assign assetLinkLocalService = serviceLocator.findService("com.liferay.asset.kernel.service.AssetLinkLocalService") /> 
547        <#assign assetEntryLocalService = serviceLocator.findService("com.liferay.asset.kernel.service.AssetEntryLocalService") /> 
548        <#assign currentArticleResourcePrimKey = currentArticle.getResourcePrimKey() /> 
549        <#assign currentArticleAssetEntry = assetEntryLocalService.getEntry("com.liferay.journal.model.JournalArticle", currentArticleResourcePrimKey) /> 
550        <#assign currentArticleAssetEntryId = currentArticleAssetEntry.getEntryId() /> 
551        <#assign currentArticleRelatedLinks = assetLinkLocalService.getDirectLinks(currentArticleAssetEntryId) /> 
552         
553 
554        <#if currentArticleRelatedLinks?has_content> 
555            <#assign relatedLinks = []> 
556            <#assign prioritizedLinks = []> 
557            <#assign alreadyHandledRelatedLinksAssetEntryIds = ""> 
558             
559            <#assign relatedLinksEntryIds = "" /> 
560            <#list currentArticleRelatedLinks as related_entry> 
561                <#assign relatedLinksEntryIds = relatedLinksEntryIds + related_entry.getEntryId2() + ", " /> 
562            </#list> 
563             
564            <#if prioritizedRelatedContent?? && prioritizedRelatedContent?has_content && prioritizedRelatedContent.getSiblings()?has_content> 
565                <#list prioritizedRelatedContent.getSiblings() as prioritizedContent> 
566                    <#assign prioritizedContentAsJSON = jsonFactoryUtil.createJSONObject(prioritizedContent.getData()) > 
567                    <#if prioritizedContentAsJSON.className??> 
568                        <#if prioritizedContentAsJSON.className == "com.liferay.journal.model.JournalArticle"> 
569                            <#assign assetEntry = assetEntryLocalService.fetchEntry(prioritizedContentAsJSON.className, prioritizedContentAsJSON.classPK?number)!"" > 
570                            <#if assetEntry?has_content && relatedLinksEntryIds?contains(assetEntry.entryId?string)> 
571                                <#assign prioritizedLinks += [getRelatedLinkObjectForAssetEntry(assetEntry.entryId)] /> 
572                                <#assign alreadyHandledRelatedLinksAssetEntryIds = alreadyHandledRelatedLinksAssetEntryIds + "," + assetEntry.entryId > 
573                            </#if> 
574                        </#if> 
575                    </#if> 
576                </#list> 
577            </#if>             
578            <#list currentArticleRelatedLinks as related_entry> 
579                <#if !alreadyHandledRelatedLinksAssetEntryIds?contains(related_entry.getEntryId2()?string)> 
580                    <#assign relatedLinks += [getRelatedLinkObjectForAssetEntry(related_entry.getEntryId2())] /> 
581                </#if> 
582            </#list> 
583            <#if relatedLinks?has_content> 
584 
585                <#-- <div class="article-assets"> 
586                    <h2 class="heading-decor-section">Lue lis&auml;&auml;:</h2> --> 
587 
588                <div class="blue-wrapper"> 
589                    <div class="container entry-columns"> 
590                        <div class="row"> 
591                            <div class="entry-column col-md-12"> 
592                                <h2 class="header--top-stripe">Toimitus suosittelee</h2> 
593                                <#assign relatedLinksSorted = prioritizedLinks + relatedLinks?sort_by("date")?reverse /> 
594                                <#list relatedLinksSorted as link> 
595 
596                                    <#-- Get entry categories --> 
597                                    <#-- <#assign categories = link.getCategories()> --> 
598                                     
599                                    <#-- Get article main images --> 
600                                    <#-- <#assign docXml = saxReaderUtil.read(link.getAssetRenderer().getArticle().getContent()) /> --> 
601                                    <#-- <#assign image = docXml.valueOf("//dynamic-element[@name='articleImages']/dynamic-content/text()") /> --> 
602 
603                                <#-- 25557 show maximum 7 related links --> 
604                                 
605                                <#if link?index gt 6> 
606                                    <#break /> 
607                                </#if> 
608                                 
609                                <div class="entry-card news-pick-list-cards"> 
610                                    <div class="row"> 
611 
612                                        <#if link?is_first> 
613                                            <div class="col col-xs-12 mb-3 col-md-4 mb-md-0"> 
614                                                <div class="xaspect-ratio xaspect-ratio-4-to-3"> 
615                                                    <#-- <img class="xaspect-ratio-item xaspect-ratio-item-center xaspect-ratio-item-middle" src="${getThumbnailUrl(image, 2) }" alt="" /> --> 
616                                                    <#if link.urlImage?? && link.urlImage?has_content > 
617                                                        <#assign imageJSON = jsonFactoryUtil.createJSONObject(link.urlImage)> 
618                                                        <#assign fileEntryId = imageJSON.getString("fileEntryId")> 
619                                                        <#assign fileName = imageJSON.getString("name")> 
620                                                        <img src="${getImageURL(imageJSON, '600')}" class="media-object"  alt=""  /> 
621                                                         
622                                                    <#else> 
623                                                        <div class="xaspect-ratio xaspect-ratio-top xaspect-ratio-3-to-2"> 
624                                                            <div class="placeholder placeholder-colored"></div>  
625                                                        </div> 
626                                                    </#if> 
627                                                </div> 
628                                            </div> 
629 
630                                        <#else> 
631                                            <div class="col col-sm-2 d-none d-md-block"> 
632                                                <div class="xaspect-ratio xaspect-ratio-4-to-3"> 
633                                                    <#-- <img class="xaspect-ratio-item xaspect-ratio-item-center xaspect-ratio-item-middle" src="${getThumbnailUrl(image, 2) }" alt="" /> --> 
634                                                    <#if link.urlImage?? && link.urlImage?has_content > 
635                                                        <#assign imageJSON = jsonFactoryUtil.createJSONObject(link.urlImage)> 
636                                                        <#assign fileEntryId = imageJSON.getString("fileEntryId")> 
637                                                        <#assign fileName = imageJSON.getString("name")> 
638                                                        <img src="${getImageURL(imageJSON, '600')}" class="media-object" /> 
639                                                         
640                                                    <#else> 
641                                                        <div class="xaspect-ratio xaspect-ratio-top xaspect-ratio-3-to-2"> 
642                                                            <div class="placeholder placeholder-colored"></div>  
643                                                        </div> 
644                                                    </#if> 
645                                                </div> 
646                                            </div> 
647                                        </#if> 
648 
649                                         
650                                        <div class="col"> 
651                                            <#if link?is_first> 
652                                                <h3 class="blue-wrapper--first-title"><a href="/-/${link.urlTitle}" class="media-content">${link.title}</a></h3> 
653                                            <#else> 
654                                                <h3><a href="/-/${link.urlTitle}" class="media-content">${link.title}</a></h3> 
655                                            </#if> 
656                                             
657                                            <#list link.categories as cat> 
658                                                <span data-cat="${cat?lower_case}" class="category-item">${cat}</span> 
659                                            </#list> 
660                                            <span class="entry-meta">${link.date}</span> 
661 
662                                            <#if link?is_first> 
663                                                <p>${link.lead}</p> 
664                                            </#if> 
665 
666                                        </div> 
667                                    </div> 
668                                    <hr> 
669                                </div> 
670 
671 
672 
673                                    <#-- VANHAT --> 
674 
675                                    <#-- <div class="media"> 
676                                        <div class="media-left"> 
677                                            <a href="/-/${link.urlTitle}" class="sans"> 
678 
679                                                <#if link.urlImage?? && link.urlImage?has_content > 
680                                                    <#assign imageJSON = jsonFactoryUtil.createJSONObject(link.urlImage)> 
681                                                    <#assign fileEntryId = imageJSON.getString("fileEntryId")> 
682                                                    <#assign fileName = imageJSON.getString("name")> 
683                                                    <img src="${getImageURL(imageJSON, '600')}" class="media-object" /> 
684                                                     
685                                                <#else> 
686                                                    <div class="aspect-ratio aspect-ratio-top aspect-ratio-3-to-2"> 
687                                                        <div class="placeholder placeholder-colored"></div>  
688                                                    </div> 
689                                                </#if> 
690 
691                                            </a> 
692                                        </div> 
693                                        <div class="media-body"> 
694                                            <a href="/-/${link.urlTitle}" class="sans">${link.title}</a> 
695                                            <span class="date">${link.date}</span> 
696                                        </div> 
697                                    </div> --> 
698                                </#list> 
699                            </div> 
700                        </div> 
701                    </div> 
702 
703                </div> 
704 
705            </#if> 
706        </#if> 
707 
708        <script src="${javascript_folder}/audioplayer.min.js?v=2"></script> 
709 
710        <#--  NEWSLETTER SUBSCRIPTION BANNER  --> 
711        <#-- <div class="article-subscription"> 
712            <div class="visible-md visible-lg"> 
713                <div class="banner-category-d"><h2 class="no-top-margin" style="margin-bottom: 30px;">Tilaa Kirkko ja kaupungin viikoittainen juttukooste</h2> 
714                    <form class="form-inline" action="https://kirkkojakaupunki.creamailer.fi/tilaa/59d75acf78807" id="subForm" method="post"> 
715                        <input id="redirect" name="redirect" type="hidden" value="https://kirkkojakaupunki-dev.ch5finland.com/"> 
716                        <input class="form-control" id="S&auml;hk&ouml;postiosoite" name="userEmail" placeholder="S&auml;hk&ouml;postiosoite"> 
717                        <input id="newsletter-submit-article" class="btn btn-inverse" type="submit" value="Tilaa uutiskirje"> 
718                    </form>  
719                </div> 
720            </div> 
721        </div> --> 
722 
723        <#-- FACEBOOK COMMENTS --> 
724        <#-- <#if articleComments?? && getterUtil.getBoolean(articleComments.getData()) == true> 
725            <div id="fb-root"></div> 
726            <script>if ($.gdprcookie.preference("analytics")) { document.write(` 
727                <scri`+`pt> 
728                    (function(d, s, id) { 
729                        var js, fjs = d.getElementsByTagName(s)[0]; 
730                        if (d.getElementById(id)) return; 
731                        js = d.createElement(s); js.id = id; 
732                        js.src = "//connect.facebook.net/fi_FI/sdk.js#xfbml=1&version=v2.8&appId=206864833121117"; 
733                        fjs.parentNode.insertBefore(js, fjs); 
734                    }(document, 'script', 'facebook-jssdk')); 
735                </`+`script> 
736                 
737                <h2 class="heading-decor-section">Kommentoi</h2> 
738                <p>Kommentoidaksesi sinun täytyy olla kirjautuneena Facebookiin.</p> 
739                <div class="fb-comments" data-href="${articleCompleteUrl}" data-width="725" data-numposts="5"></div> 
740                 
741            `); } 
742        </script> 
743 
744        </#if> --> 
745  </div> 
746</article> 
747 
748 
749<#-- Include script only if owl carousel among content has been built --> 
750 
751<script src="${javascript_folder}/owl.carousel.min.js"></script> 
752 
753<#if includeOwlScript == true> 
754    <script> 
755    	$('.owl-carousel').owlCarousel({ 
756            items: 1, 
757    		loop: true, 
758    		rewind: false, 
759    		margin: 0, 
760    		nav: true, 
761            navText : ['<span class="owl-carousel-nav glyphicon glyphicon-chevron-left"></span>','<span class="owl-carousel-nav glyphicon glyphicon-chevron-right"></span>'], 
762            autoplay:true, 
763            autoplayTimeout:3000, 
764            smartSpeed:1400, 
765            autoplayHoverPause:true 
766    	}); 
767 
768    </script> 
769</#if> 
770 
771<#-- 
772    Convert Youtube links to embedded videos 
773--> 
774<script type="text/javascript"> 
775 
776    $(document).ready(function () { 
777        $(".js-video-embed" ).each(function() { 
778            // Creates an iframe if it's youtube video. Otherwise just shows the original link. 
779            if (getYoutubeVideoId($(this).attr("href"))) { 
780                var div = $("<div>", {"class": "responsive-video"}); 
781                var wrapper = $("<div>", {"class": "responsive-wrapper"}); 
782                wrapper.append('<iframe src="https://www.youtube.com/embed/'+getYoutubeVideoId($(this).attr("href"))+'?rel=0" frameborder="0" allowfullscreen=""></iframe>'); 
783                div.append(wrapper); 
784                $(this).parent().append(div); 
785                $(this).hide(); //iframe is shown, hide the original link 
786
787        }); 
788    }); 
789    function getYoutubeVideoId(url) { 
790        var videoid = url.match(/(?:https?:\/{2})?(?:w{3}\.)?youtu(?:be)?\.(?:com|be)(?:\/watch\?v=|\/)([^\s&]+)/); 
791        if(videoid != null) { 
792           return videoid[1]; 
793        } else { 
794            return ""; 
795
796
797</script> 
798 
799<#--   
800    Create image comparison slider   
801--> 
802<script src="/o/kirkkojakaupunki-site-theme/js/cocoen.min.js"></script> 
803 
804<script type="text/javascript"> 
805    $(document).ready(function () { 
806        document.querySelectorAll('.cocoen').forEach(function(element){ 
807            var imageComparison = new Cocoen(element); 
808                var sliderOffset = $(element).attr("slider-offset"); 
809                if ($(element).attr("slider-offset") >= 100 || !($(element).attr("slider-offset"))){ 
810                    sliderOffset = 98; 
811                } else if ($(element).attr("slider-offset") == 0) { 
812                    sliderOffset = 2; 
813
814                imageComparison.element.children[0].style.width = sliderOffset+"%"; 
815                imageComparison.dragElement.style.left = sliderOffset+"%"; 
816        }); 
817 
818        // depending on slider offset show either the caption of the first or the second image 
819        $('.cocoen').mousemove(function(event){ 
820            addCaption(this); 
821        }); 
822 
823        // same functionality for touch screens 
824        document.querySelectorAll('.cocoen').forEach(function(element){ 
825            element.addEventListener("touchmove", function() { 
826                addCaption(this); 
827            }); 
828        }); 
829 
830        function addCaption(element) { 
831            var index = $('.cocoen').index(element); 
832            var sliderOffset = parseInt(element.firstElementChild.style.width); 
833            if(sliderOffset < 50){ 
834                $('.caption1').eq(index).hide(); 
835                $('.caption2').eq(index).show(); 
836            } else { 
837                $('.caption2').eq(index).hide(); 
838                $('.caption1').eq(index).show(); 
839
840
841    });     
842     
843</script> 
844 
845 
846<#----------------------------------------------------------------------------- 
847    Helper functions 
848------------------------------------------------------------------------------> 
849<#function getRelatedLinkObjectForAssetEntry relatedAssetEntryId> 
850                <#local relatedAssetEntry = assetEntryLocalService.getEntry(relatedAssetEntryId) /> 
851                <#local relatedAssetEntryPrimKey = relatedAssetEntry.getClassPK() /> 
852                <#local relatedArticle = JournalArticleLocalService.getLatestArticle(relatedAssetEntryPrimKey) /> 
853                <#local relatedArticleId = relatedArticle.getArticleId() /> 
854                <#local docXml = saxReaderUtil.read(relatedArticle.getContent()) />	 
855                <#local image = docXml.valueOf("//dynamic-element[@name='articleImages']/dynamic-content/text()") /> 
856 
857                <#-- Get full description & shorten it --> 
858                <#local lead = htmlUtil.stripHtml(htmlUtil.extractText(relatedArticle.getDescription())) /> 
859         
860                <#local categories = getCategoryNames(relatedArticleId) /> 
861 
862                <#local linkObject = {"categories": categories, "title": relatedArticle.getTitleCurrentValue(),"urlTitle": relatedArticle.getUrlTitle(), "date": relatedArticle.getDisplayDate()?date, "urlImage": image, "lead": lead } /> 
863                 
864                <#if image?? && image?has_content> 
865 
866                <#else> 
867                    <#local nostokuva = docXml.valueOf("//dynamic-element[@name='nostokuva']/dynamic-content/text()") /> 
868                    <#if nostokuva?? && nostokuva?has_content> 
869                        <#local image = nostokuva /> 
870                        <#local linkObject = {"title": relatedArticle.getTitleCurrentValue(),"urlTitle": relatedArticle.getUrlTitle(), "date": relatedArticle.getDisplayDate()?date, "urlImage": image, "lead": lead, "categories": categories  } /> 
871                    </#if> 
872                </#if> 
873    <#return linkObject /> 
874</#function> 
875 
876<#macro printRemainingTagContents container startAt wrapperCSSClass> 
877    <#local contentSlots = container.getSiblings() /> 
878    <#if contentSlots?has_content && contentSlots?size gt 0> 
879        <#list contentSlots as slot> 
880            <#if slot?index gte startAt> 
881            <#if slot.getData()?? && slot.getData()?has_content> 
882            <#assign slotData = slot.getData()> 
883               <#if articleImages?has_content> 
884                <#assign slotData = replaceImageTags(slotData, tagImage, articleImages)> 
885                <#assign slotData = replaceCarouselTags(slotData, tagCarousel, articleImages)> 
886            </#if> 
887        	    <#switch wrapperCSSClass> 
888        	        <#case 'template-quote'> 
889        	            <div class="article-box article-quote voice-no-read"> 
890        	                <p class="quote-body">${slotData}</p> 
891        	            </div> 
892                	    <#break> 
893            	    <#default> 
894            	        <div class="${wrapperCSSClass}">${slotData}</div> 
895        	    </#switch> 
896            </#if> 
897            </#if> 
898        </#list> 
899    </#if> 
900</#macro> 
901 
902 
903<#-- 
904    Print remainging videos, if any 
905--> 
906<#macro printRemainingVideoContents container startAt> 
907    <#local contentSlots = container.getSiblings() /> 
908    <#if contentSlots?has_content && contentSlots?size gt 0> 
909        <#list contentSlots as slot> 
910            <#if slot?index gte startAt> 
911                <#if slot.URL.getData()?? && slot.URL.getData()?has_content> 
912 
913                    <#assign URL = slot.URL.getData()!"" /> 
914                    <#assign videoFile = slot.videoFile.getData()!"" /> 
915                    <#assign caption = slot.caption.getData()!"" /> 
916                    <#assign embed = videoEmbed(URL, caption, videoFile)!"" /> 
917                    ${embed} 
918 
919                </#if> 
920            </#if> 
921        </#list> 
922    </#if> 
923</#macro> 
924 
925<#-- 
926    Print remainging audio embeds, if any 
927--> 
928<#macro printRemainingAudioContents container startAt> 
929    <#local contentSlots = container.getSiblings() /> 
930    <#if contentSlots?has_content && contentSlots?size gt 0> 
931        <#list contentSlots as slot> 
932            <#if slot?index gte startAt> 
933                <#if slot.audioFile.getData()?? && slot.audioFile.getData()?has_content> 
934                    <#assign description = slot.audioDescription.getData() /> 
935                    <#assign files = slot.audioFile /> 
936                    <#assign embed = audioEmbed(description, files) /> 
937                    ${embed} 
938                </#if> 
939            </#if> 
940        </#list> 
941    </#if> 
942</#macro> 
943 
944<#function replaceTags content tag replacementElement wrapperCSSClass> 
945    <#global tagReplaceCounter = 0 /> 
946    <#if replacementElement?has_content> 
947        <#assign replacementList = replacementElement.getSiblings() > 
948        <#if replacementList?has_content> 
949 
950        	<#list replacementList as cur_replacement> 
951 
952        	    <#switch wrapperCSSClass> 
953        	        <#case 'template-quote'> 
954                	    <#assign replacement> 
955                	        <div class="article-box article-quote voice-no-read"> 
956                	            <p class="quote-body">${cur_replacement.getData()}</p> 
957            	            </div> 
958                	    </#assign> 
959                	    <#break> 
960        	        <#case 'no-wrapper'> 
961                	    <#assign replacement = cur_replacement.getData() > 
962                	    <#break>                	     
963            	    <#default> 
964                	    <#assign replacement> 
965                	        <div class="${wrapperCSSClass}">${cur_replacement.getData()}</div> 
966                	    </#assign> 
967        	    </#switch> 
968 
969        	    <#if content?index_of(tag) gt -1> 
970            		<#local content = content?replace(tag, replacement, 'f') > 
971            		<#global tagReplaceCounter++ /> 
972        		</#if> 
973        	</#list> 
974        </#if> 
975    </#if> 
976 
977  <#-- Remove "empty" tags--> 
978  <#local content = content?replace(tag, '') > 
979 
980  <#return content > 
981</#function> 
982 
983<#function replaceCarouselTags content tagBase replacementElement> 
984    <#if replacementElement?has_content> 
985        <#assign replacementList = replacementElement.getSiblings()> 
986 
987        <#if replacementList?has_content> 
988            <#list 1..10 as i> 
989                <#assign carouselArray = []> 
990                <#list replacementList as cur_image> 
991                    <#if cur_image.getData?? && cur_image.getData()?has_content> 
992                        <#if cur_image.position.getData() == "article-image-carousel-${i}"> 
993                            <#assign carouselArray = carouselArray + [createCarouselItem(cur_image)] /> 
994                        </#if> 
995                    </#if> 
996                </#list> 
997                 
998                <#if carouselArray?has_content> 
999                    <#assign replacement> 
1000                        <div class="owl-carousel owl-theme"> 
1001                            <#list carouselArray as carousel1> 
1002                            <div class="item"> 
1003                                ${carousel1} 
1004                            </div> 
1005                            </#list> 
1006                        </div> 
1007                    </#assign> 
1008                    <#local content = content?replace(tagBase?replace("#", i), replacement, 'f') > 
1009                </#if> 
1010            </#list> 
1011        </#if> 
1012    </#if> 
1013     
1014     
1015    <#-- clean image carousel tags --> 
1016    <#list 1..10 as i> 
1017        <#local content = content?replace(tagBase?replace("#", i), "") > 
1018    </#list> 
1019    <#return content /> 
1020</#function> 
1021 
1022<#function createCarouselItem cur_image> 
1023    <#assign result = "" /> 
1024    <#if cur_image.getAttribute("alt")?has_content> 
1025        <#assign result> 
1026            <div class="aspect-ratio aspect-ratio-middle aspect-ratio-xs"> 
1027                <img class="aspect-ratio-item-fluid" src="${getImageURLByTemplateModel(cur_image, '1200')}" alt="${cur_image.getAttribute("alt")}" /> 
1028            </div> 
1029            <p class="caption voice-no-read">${cur_image.getAttribute("alt")}</p> 
1030        </#assign> 
1031    <#else> 
1032        <#assign result> 
1033            <div class="aspect-ratio aspect-ratio-middle aspect-ratio-xs"> 
1034                <img class="aspect-ratio-item-fluid" src="${getImageURLByTemplateModel(cur_image, '1200')}" alt="" /> 
1035            </div> 
1036        </#assign> 
1037    </#if> 
1038    <#return result /> 
1039</#function> 
1040 
1041<#function replaceImageTags content tag replacementElement> 
1042    <#if replacementElement?has_content> 
1043        <#assign replacementList = replacementElement.getSiblings() > 
1044        <#if replacementList?has_content> 
1045        	<#list replacementList as cur_image> 
1046 
1047                <#if cur_image.getData?? && cur_image.getData()?has_content> 
1048 
1049                    <#-- Skip header images --> 
1050                    <#if cur_image.position.getData() != "article-image-header" && !cur_image.position.getData()?starts_with("article-image-carousel-")> 
1051                        <#assign thumbnailSize = 2 > 
1052                        <#if cur_image.position.getData() == "article-image-full"> 
1053                            <#assign thumbnailSize = 3 > 
1054                        </#if> 
1055                        <#assign replacement> 
1056 
1057                            <div class="article-box ${cur_image.position.getData()}"> 
1058                                <#if cur_image.getAttribute("alt")?has_content> 
1059                                    <img src="${getImageURLByTemplateModel(cur_image, '1200')}" alt="${cur_image.getAttribute("alt")}" /> 
1060                                    <p class="caption voice-no-read">${cur_image.getAttribute("alt")}</p> 
1061                                <#else> 
1062                                    <img src="${getImageURLByTemplateModel(cur_image, '1200')}" alt="" /> 
1063                                </#if> 
1064                            </div> 
1065                        </#assign> 
1066                        <#local content = content?replace(tag, replacement, 'f') > 
1067                    </#if> 
1068 
1069                </#if> 
1070 
1071        	</#list> 
1072        </#if> 
1073    </#if> 
1074 
1075  <#-- Remove "empty" tags--> 
1076  <#local content = content?replace(tag, '') > 
1077 
1078  <#return content > 
1079</#function> 
1080 
1081 
1082<#function replaceImageComparisonTags content tag replacementElement> 
1083    <#if replacementElement?has_content> 
1084        <#assign replacementList = replacementElement.getSiblings() > 
1085        <#if replacementList?has_content> 
1086            <#list replacementList as cur_imageComparison> 
1087                <#if cur_imageComparison.getData??> 
1088                    <#assign image1 = cur_imageComparison.image1 /> 
1089                    <#assign image2 = cur_imageComparison.image2 /> 
1090                    <#if cur_imageComparison.sliderOffset.getData() == "" > 
1091                        <#assign offset = 100 /> 
1092                    <#else> 
1093                        <#assign offset = cur_imageComparison.sliderOffset.getData() /> 
1094                    </#if> 
1095                    <#assign replacement> 
1096                        <#if image1.getData?? && image1.getData()?has_content && image2.getData?? && image2.getData()?has_content> 
1097                            <div class="cocoen-theme"> 
1098                                <div class="cocoen" slider-offset="${offset}"> 
1099                                    <#if image1.getAttribute("alt")?has_content> 
1100                                        <img src="${getThumbnailUrl(image1.getData(), 3) }" alt="${image1.getAttribute("alt")}" /> 
1101                                    <#else> 
1102                                        <img src="${getThumbnailUrl(image1.getData(), 3) }" alt="" /> 
1103                                    </#if> 
1104                                    <#if image2.getAttribute("alt")?has_content> 
1105                                        <img src="${getThumbnailUrl(image2.getData(), 3) }" alt="${image2.getAttribute("alt")}" /> 
1106                                    <#else> 
1107                                        <img src="${getThumbnailUrl(image2.getData(), 3) }" alt="" /> 
1108                                    </#if> 
1109                                </div> 
1110                                <#if image1.getAttribute("alt")?has_content && image2.getAttribute("alt")?has_content> 
1111                                    <#if offset?number lt 50> <!-- pienempi kuin 50, n&auml;yt&auml; kuvateksti 2 --> 
1112                                        <div class="caption2"> 
1113                                            <p class="caption voice-no-read"> 
1114                                                ${image2.getAttribute("alt")} 
1115                                            </p> 
1116                                        </div> 
1117                                        <div class="caption1" style="display:none"> 
1118                                            <p class="caption voice-no-read"> 
1119                                                ${image1.getAttribute("alt")} 
1120                                            </p> 
1121                                        </div> 
1122                                    <#else> 
1123                                        <div class="caption2" style="display:none"> 
1124                                            <p class="caption voice-no-read"> 
1125                                                ${image2.getAttribute("alt")} 
1126                                            </p> 
1127                                        </div> 
1128                                        <div class="caption1"> 
1129                                            <p class="caption voice-no-read"> 
1130                                                ${image1.getAttribute("alt")} 
1131                                            </p> 
1132                                        </div> 
1133                                    </#if> 
1134                                </#if> 
1135                            </div> 
1136                        </#if> 
1137                    </#assign> 
1138                    <#local content = content?replace(tag, replacement, 'f') > 
1139                </#if> 
1140            </#list> 
1141        </#if> 
1142    </#if> 
1143    <#-- Remove "empty" tags--> 
1144    <#local content = content?replace(tag, '') > 
1145 
1146    <#return content > 
1147</#function> 
1148 
1149 
1150<#function replaceVideoTags content tag replacementElement> 
1151    <#global tagReplaceCounter = 0 /> 
1152    <#if replacementElement?has_content> 
1153        <#assign replacementList = replacementElement.getSiblings() > 
1154        <#if replacementList?has_content> 
1155 
1156            <#global videoListSize = replacementList?size /> 
1157 
1158        	<#list replacementList as cur_video> 
1159                <#assign videoFile = "" /> 
1160                <#assign URL = cur_video.URL.getData()!"" /> 
1161                <#if (cur_video.videoFile)??> 
1162                    <#assign videoFile = cur_video.videoFile.getData()!"" /> 
1163                </#if> 
1164                <#assign caption = cur_video.caption.getData()!"" /> 
1165                 
1166                <#if (cur_video.videoFile)?? || URL?has_content> 
1167                    <#assign replacement = videoEmbed(URL, caption, videoFile)!"" /> 
1168                </#if> 
1169                
1170                <#if content?index_of(tag) gt -1> 
1171                    <#local content = content?replace(tag, replacement, 'f') > 
1172                    <#global tagReplaceCounter++ /> 
1173                </#if> 
1174 
1175                <#-- <#local content = content?replace(tag, replacement, 'f') > --> 
1176        	</#list> 
1177        </#if> 
1178    </#if> 
1179 
1180  <#-- Remove "empty" tags--> 
1181  <#local content = content?replace(tag, '') > 
1182 
1183  <#return content > 
1184</#function> 
1185 
1186 
1187<#function replaceAudioTags content tag replacementElement> 
1188    <#global tagReplaceCounter = 0 /> 
1189    <#if replacementElement?has_content> 
1190        <#assign replacementList = replacementElement.getSiblings() > 
1191        <#if replacementList?has_content> 
1192 
1193            <#global audioListSize = replacementList?size /> 
1194 
1195        	<#list replacementList as cur_audioEmbed> 
1196                <#assign description = cur_audioEmbed.audioDescription.getData() /> 
1197                <#assign files = cur_audioEmbed.audioFile /> 
1198                <#assign replacement = audioEmbed(description, files) /> 
1199 
1200                <#if content?index_of(tag) gt -1> 
1201                    <#local content = content?replace(tag, replacement, 'f') > 
1202                    <#global tagReplaceCounter++ /> 
1203                </#if> 
1204 
1205        	</#list> 
1206        </#if> 
1207    </#if> 
1208    <#local content = content?replace(tag, '') > 
1209    <#return content > 
1210</#function> 
1211 
1212<#function replaceAdTag content> 
1213    <#local adScript> 
1214    	<div class="ad-container"> 
1215            <div> 
1216                <script type="text/javascript"> 
1217                if (!$.gdprcookie || $.gdprcookie.preference("marketing")) { 
1218                    if((window.innerWidth > 1019) || (typeof inPreviewMode !== 'undefined' && inPreviewMode)) 
1219
1220                    // NM 468x400 
1221                    document.write('<scri'+'pt data-adfscript="adx.adform.net/adx/?mid=937873&mkv=" id="ar"></'+'script>'); 
1222                    document.write('<scri'+'pt src="//s1.adform.net/banners/scripts/adx.js" async defer ></'+'script>'); 
1223
1224                    else 
1225
1226                    // NM 300x300 3 
1227                    document.write('<scri'+'pt data-adfscript="adx.adform.net/adx/?mid=937882&mkv="></'+'script>'); 
1228                    document.write('<scri'+'pt src="//s1.adform.net/banners/scripts/adx.js" async defer></'+'script>'); 
1229
1230
1231                </script> 
1232            </div> 
1233        </div> 
1234    </#local> 
1235     
1236    <#return content?replace("[[mainos]]", adScript) /> 
1237</#function> 
1238 
1239<#function videoEmbed URL, caption, videoFile> 
1240    <#local embed = "" /> 
1241 
1242    <#if URL?contains("vimeo")> 
1243        <#local iframeSrc = "https://player.vimeo.com/video/" /> 
1244        <#local videoID = URL?split("/")?last /> 
1245		<#local embed> 
1246		    <div class="responsive-video"> 
1247    			<div class="responsive-wrapper vimeo" id="vimeo-${videoID}" data-embed="${videoID}"> 
1248    				<img class="decor-video" alt="Toista video" src="/o/kirkkojakaupunki-site-theme/images/icons/video-decor.svg"> 
1249    			</div> 
1250				<noscript><a href="${URL}" target="_blank" rel="noopener"Videota</a> ei voida n&auml;ytt&auml;&auml; koska javascript on kytketty pois p&auml;&auml;lt&auml; selaimesta.</noscript> 
1251    		</div> 
1252		</#local> 
1253    <#elseif URL?contains("youtube")> 
1254        <#local isVerticalVideo = URL?starts_with("https://www.youtube.com/shorts/")> 
1255         
1256        <#local iframeSrc = "https://www.youtube.com/embed/" /> 
1257        <#local videoID = URL?replace("^.*\\?v=([\\w-_]*).*", "$1", "r") /> 
1258        <#if URL?starts_with("https://www.youtube.com/shorts/")> 
1259            <#local videoID = URL?replace("https://www.youtube.com/shorts/","")> 
1260        </#if> 
1261         
1262		<#local embed> 
1263             
1264            <div class="responsive-video <#if isVerticalVideo>vertical-video</#if>">   
1265    			<div class="responsive-wrapper youtube" data-embed="${videoID}"> 
1266    				<img class="decor-video" alt="Toista video" src="/o/kirkkojakaupunki-site-theme/images/icons/video-decor.svg"> 
1267    			</div> 
1268                <noscript><a href="https://www.youtube.com/watch?v=${videoID}" target="_blank" rel="noopener">Videota</a> ei voida n&auml;ytt&auml;&auml; koska javascript on kytketty pois p&auml;&auml;lt&auml; selaimesta.</noscript> 
1269			</div> 
1270		</#local> 
1271    <#elseif videoFile?has_content> 
1272        <#local embed> 
1273            <div class="responsive-video"> 
1274                <video controls controlsList="nodownload" style="max-width:100%"> 
1275                <#-- #t=0.001 Safari hack - See ticket 25143 --> 
1276                <source src="${videoFile}#t=0.001" type="video/mp4"> 
1277                    Your browser does not support the video tag. 
1278                </video> 
1279            </div>  
1280        </#local> 
1281    <#else> 
1282        <#return embed> 
1283    </#if> 
1284 
1285    <#return embed> 
1286</#function> 
1287 
1288 
1289<#function audioEmbed description, files> 
1290    <#local embed = ""> 
1291    <#local audioFiles = [] /> 
1292     
1293    <#list files.getSiblings() as file>        
1294        <#local fileURL = file.getData()!""> 
1295        <#if fileURL?has_content> 
1296            <#local fileFormat = file.audioFormat.getData()> 
1297            <#local fileObject = {"format": fileFormat, "URL": fileURL}> 
1298            <#local audioFiles += [fileObject]> 
1299        </#if> 
1300    </#list> 
1301 
1302    <#if audioFiles?has_content> 
1303        <#local embed> 
1304            <div class="audio-player sans voice-no-read"> 
1305                <button class="btn btn-link flex-item-center"> 
1306                    <i class="glyphicon glyphicon-play" aria-hidden="true"></i> 
1307                </button> 
1308                <div class="audio-player-details flex-item-center"> 
1309                    <#if description?has_content> 
1310                        <p>${description}</p> 
1311                    </#if> 
1312                    <div class="audio-player-progress"> 
1313                        <span class="audio-player-time-display">-:- / -:-</span> 
1314                        <div class="audio-player-seekbar"> 
1315                            <div class="audio-player-bufferbar"></div> 
1316                            <div class="audio-player-progressbar"></div> 
1317                        </div> 
1318                    </div>                 
1319                </div> 
1320                <audio preload="metadata" loop> 
1321                    <#list audioFiles as file> 
1322                        <source src="${file.URL}" type="${file.format}" /> 
1323                    </#list> 
1324                </audio> 
1325            </div> 
1326        </#local> 
1327    </#if> 
1328 
1329    <#return embed> 
1330</#function> 
1331 
1332 
1333<#function getHeaderImages articleImages> 
1334    <#local headerImages = [] /> 
1335 
1336    <#if articleImages?has_content && articleImages.getSiblings()?has_content> 
1337        <#list articleImages.getSiblings() as cur_image> 
1338            <#if cur_image.position.getData() == 'article-image-header'> 
1339                <#local headerImages = headerImages + [cur_image] /> 
1340            </#if> 
1341        </#list> 
1342    </#if> 
1343 
1344    <#if !headerImages?has_content && articleImages?has_content && articleImages.getSiblings()?has_content > 
1345        <#local headerImages = headerImages + [articleImages.getSiblings()?first] /> 
1346    </#if> 
1347 
1348    <#return headerImages> 
1349</#function> 
1350 
1351<#function getContentCarouselImages articleImages> 
1352 
1353    <#local contentCarouselImages = [] /> 
1354 
1355    <#if articleImages?has_content && articleImages.getSiblings()?has_content> 
1356        <#list articleImages.getSiblings() as cur_image> 
1357            <#if cur_image.position.getData()?starts_with('article-image-carousel-')> 
1358                <#local contentCarouselImages = contentCarouselImages + [cur_image] /> 
1359            </#if> 
1360        </#list> 
1361    </#if> 
1362 
1363    <#if !contentCarouselImages?has_content && articleImages?has_content && articleImages.getSiblings()?has_content > 
1364        <#local contentCarouselImages = contentCarouselImages + [articleImages.getSiblings()?first] /> 
1365    </#if> 
1366 
1367    <#return contentCarouselImages> 
1368</#function> 
1369 
1370<#-- 
1371    Attempts to get mime type for a file in document library. 
1372 
1373    Parameters: 
1374        fileUrl: A string containig a url to the file. 
1375 
1376    Returns mime type as a string or an empty string. 
1377--> 
1378<#function getMimeType fileUrl> 
1379    <#if fileUrl?? && fileUrl?has_content> 
1380 
1381        <#-- Attempt to parse uuid & groupId from provided Url --> 
1382        <#assign splitUrl = fileUrl?split("/", "r") /> 
1383 
1384        <#if splitUrl?seq_contains("documents") && splitUrl?size gte 6> 
1385            <#attempt> 
1386 
1387                <#assign groupId = splitUrl[2] /> 
1388                <#assign uuid = splitUrl[5] /> 
1389                <#assign uuid = uuid?split("?")[0] /> 
1390 
1391                <#-- Get DLFileEntry --> 
1392                <#assign DLFileEntryLocalService = serviceLocator.findService("com.liferay.document.library.kernel.service.DLFileEntryLocalService") /> 
1393                <#assign fileEntry = DLFileEntryLocalService.fetchDLFileEntryByUuidAndGroupId(uuid, getterUtil.getLong(groupId)) /> 
1394                <#return fileEntry.getMimeType()> 
1395            <#recover> 
1396                <#-- Parsing mime type failed, return empty --> 
1397                <#return ""> 
1398            </#attempt> 
1399 
1400        <#elseif splitUrl?seq_contains("journal") && splitUrl?size gte 4> 
1401            <#attempt> 
1402                <#assign imageId = splitUrl[3]?split("?")[1]?split("=")[1]?split("&")[0] /> 
1403                <#assign ImageLocalService = serviceLocator.findService("com.liferay.portal.kernel.service.ImageLocalService") /> 
1404                <#assign imageType = ImageLocalService.getImage(getterUtil.getLong(imageId)).getType() /> 
1405                <#switch imageType> 
1406                    <#case "gif"> 
1407                        <#return "image/gif"> 
1408                    <#case "svg"> 
1409                        <#return "image/svg"> 
1410                    <#default> 
1411                        <#return imageType> 
1412                </#switch> 
1413            <#recover> 
1414                <#return ""> 
1415            </#attempt> 
1416 
1417        <#else> 
1418            <#-- Parsing image url failed, return empty --> 
1419            <#return ""> 
1420        </#if> 
1421 
1422    <#else> 
1423        <#-- parameter empty or missing, return empty --> 
1424        <#return ""> 
1425    </#if> 
1426</#function> 
1427 
1428<#-- 
1429    Returns thumbnail image url for given image. 
1430    Handles SVG & bitmap formats. 
1431 
1432    Parameters: 
1433        imageUrl: string containing relative url to an image in document library 
1434        thumbnailSize: value for Liferay's imageThumbnail parameter (0-3) 
1435 
1436    Returns image url with appended thumbnail info or an empty string. 
1437 --> 
1438<#function getThumbnailUrl imageUrl thumbnailSize> 
1439    <#if imageUrl?? && imageUrl?has_content> 
1440 
1441        <#-- Attempt to get mime type --> 
1442        <#assign mimeType = getMimeType(imageUrl) /> 
1443 
1444        <#if mimeType?has_content> 
1445 
1446            <#-- Check image mime type --> 
1447            <#switch mimeType> 
1448                <#case "image/svg+xml"> 
1449                    <#-- SVG image -> do not append thumbnail parameter --> 
1450                    <#return imageUrl> 
1451                <#case "image/gif"> 
1452                    <#return imageUrl> 
1453                <#default> 
1454                    <#-- Predume bitmap image -> append thumbnail parameter --> 
1455                    <#if imageUrl?contains("?")> 
1456                        <#assign paramChar = "&" /> 
1457                    <#else> 
1458                        <#assign paramChar = "?" /> 
1459                    </#if> 
1460<#-- 
1461    Liferay 7.3 thumbSize 3 on pienempi kuin aiemmin. Woodwingist tulevat kuvat nykyisin 1200 joten voidaan palauttaa suoraan kuvan url. Vaihtoehto hyodyntaa adaptive mediaa 
1462--> 
1463                    <#if thumbnailSize == 3> 
1464                        <#return imageUrl /> 
1465                    <#else> 
1466                        <#return imageUrl + paramChar + "imageThumbnail=" + thumbnailSize /> 
1467                    </#if> 
1468            </#switch> 
1469        <#else> 
1470            <#-- Embbedded journal article image (type: journal) mime recognizion fails for ie  
1471            /-/sini-mikkola-tutkii-lutherin-kasityksia 
1472            /-/kristinuskon-alkeita-ja-syventavia-keskusteluja 
1473            /-/mika-keisarin-on 
1474            --> 
1475            <#return imageUrl > 
1476        </#if> 
1477 
1478    </#if> 
1479</#function> 
1480 
1481<#-- 
1482    Return list of web content titles from given folder 
1483    parameter: 
1484        - authorCardFolderId 
1485--> 
1486<#function getAvailableAuthorNames authorCardFolderIds > 
1487 
1488  <#local authorNames = []>     
1489  <#local seenArticles = [] > 
1490  <#-- Multiple version exists so keep track which articles we have already checked --> 
1491 
1492  <#list authorCardFolderIds as authorCardFolderId> 
1493    <#assign authorCards = JournalArticleLocalService.getArticles(groupId, authorCardFolderId) /> 
1494 
1495    <#if authorCards?has_content> 
1496        <#list authorCards as authorCard> 
1497	    <#if !seenArticles?seq_contains(authorCard.articleId)> 
1498	      <#assign latest = JournalArticleLocalService.getLatestArticle(authorCard.resourcePrimKey) /> 
1499	      <#if latest.getStatus() == 0> 
1500		  <#local authorName = latest.getTitle(locale) /> 
1501		  <#if !authorNames?seq_contains(authorName) > 
1502		      <#local authorNames = authorNames + [authorName] /> 
1503		  </#if> 
1504	      </#if> 
1505	      <#local seenArticles = seenArticles + [authorCard.articleId] /> 
1506	    </#if> 
1507        </#list> 
1508         
1509    </#if> 
1510  </#list> 
1511  <#return authorNames /> 
1512</#function> 
1513 
1514<#-- 
1515    Replaces from given string author names with links to author page 
1516    parameters:  
1517    - authorNameStr : String where replacements is targeted 
1518    - authorNames : seq : list of author names 
1519    - baseUrl : url where author name is appended (as url escaped string) 
1520--> 
1521 
1522<#function makeAuthorLinks authorNameStr authorNames baseUrl > 
1523    <#list authorNames as authorName> 
1524        <#local authorNameInUrl = authorName?url("utf-8") /> 
1525        <#local authorNameStr = authorNameStr?replace(authorName, "<a class=\"author-name\" href=\""+baseUrl +authorNameInUrl + "\">"+authorName+"</a>") /> 
1526    </#list> 
1527    <#return authorNameStr /> 
1528</#function> 
1529 
1530<#function getImageURL imageJSON thumbId> 
1531    <#if imageJSON?? && imageJSON?has_content> 
1532        <#assign fileEntryId = imageJSON.getLong("fileEntryId")/> 
1533        <#assign fileName = imageJSON.getString("name")/> 
1534         
1535        <#if fileName?ends_with(".svg") || fileName?ends_with(".gif")> 
1536            <#assign imageGroupId = imageJSON.getLong("groupId")/> 
1537            <#assign imageUuid = imageJSON.getString("uuid")/> 
1538         
1539            <#return getFullImageUrL(imageGroupId, imageUuid) > 
1540        <#else> 
1541            <#return getAdaptiveImageURLByFileEntryId(fileEntryId, fileName, thumbId) > 
1542        </#if> 
1543    </#if> 
1544</#function> 
1545 
1546<#function getImageURLByTemplateModel imageTemplateModel thumbId> 
1547    <#local uuid = imageTemplateModel.getAttribute("uuid")/> 
1548    <#return getFullImageUrL(groupId, uuid)> 
1549</#function> 
1550 
1551<#function getAdaptiveImageURLByFileEntryId fileEntryId fileName thumbId> 
1552 
1553		<#local adaptiveMediaUrl = "/o/adaptive-media/image/${fileEntryId}/${thumbId}/${fileName}" > 
1554		 
1555        <#return adaptiveMediaUrl> 
1556</#function> 
1557 
1558<#function getFullImageUrL(groupId, uuid)> 
1559            <#assign imageURL = "/documents/" /> 
1560            <#assign imageURL += groupId /> 
1561            <#assign imageURL += "/" + uuid /> 
1562         
1563            <#return imageURL> 
1564</#function> 
1565 
1566<#function getCategoryNames( articleId, vocabularyId="" )> 
1567 
1568    <#local categoryNames = [] /> 
1569 
1570    <#-- Get all categories from journalArticle --> 
1571    <#local journalArticleLocalService = serviceLocator.findService("com.liferay.journal.service.JournalArticleLocalService") /> 
1572    <#local primKey = journalArticleLocalService.getArticle(getterUtil.getLong(groupId), articleId).getResourcePrimKey() /> 
1573    <#local assetCategoryLocalService = serviceLocator.findService("com.liferay.asset.kernel.service.AssetCategoryLocalService") /> 
1574    <#local categories = assetCategoryLocalService.getCategories('com.liferay.journal.model.JournalArticle', getterUtil.getLong(primKey)) /> 
1575 
1576    <#if categories?has_content> 
1577        <#list categories as cur_category> 
1578            <#if vocabularyId?has_content && vocabularyId == cur_category.getVocabularyId() > 
1579                <#-- get category names from given category --> 
1580                <#local categoryNames += [cur_category.getName()] /> 
1581            <#elseif !vocabularyId?has_content> 
1582                <#-- get all category names --> 
1583                <#local categoryNames += [cur_category.getName()] /> 
1584            </#if> 
1585 
1586        </#list> 
1587    </#if> 
1588 
1589    <#return categoryNames> 
1590</#function> 
1591 
1592<#if mobileAppView> 
1593    <script> 
1594        <#-- Replace article urls (contains /-/) with this asset publisher content url --> 
1595        let contentUrlIndex = window.location.pathname.indexOf('/content/'); 
1596         
1597        if (contentUrlIndex > 0) { 
1598            let assetPublisherContentURL = window.location.pathname.substring(0,contentUrlIndex) + "/content/"; 
1599         
1600            $('article a[href*="/-/"]').each(function(){ 
1601                let localAssetPublisherContentUrl  = $(this).attr("href").replace("/-/", assetPublisherContentURL); 
1602                $(this).attr("href", localAssetPublisherContentUrl); 
1603            }); 
1604
1605    </script> 
1606</#if> 

Löydä lisää näkökulmia


Keskustele Facebookissa
Keskustele ja kommentoi Facebookissa
Lähetä juttuvinkki
Lähetä juttuvinkki
Kirkko ja kaupunki -mediaan.

Tilaa Kirkko ja kaupungin viikoittainen juttukooste.