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