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