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