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