
Onko palveluillamme vaikutusta? - blogi
Blogisarja kertoo inspiroivia esimerkkejä Pirkanmaan hyvinvointialueen palveluiden kehittämisestä ja vaikuttavuustyöstä eri palveluissa.
Niukkuus ja epävarmuus arjessa ovat lisääntyneet – Pirkanmaan hyvinvointialue ja Ihmisoikeusliitto kartoittivat sosiaaliturvan saajien kokemuksia
Uutinen
16.6.2026
Aluevaltuusto valitsi Teemu-Taavetti Toivosen pelastusjohtajaksi
Uutinen
15.6.2026
Tarkastuslautakunta: Pirhassa tehdään paljon valtuustoaloitteita
Uutinen
12.6.2026
Virhe tapahtui prosessoidessa esitysmallia.
Failed to "?eval" string with this error:
---begin-message---
Syntax error in ?eval-ed string in line 1, column 77:
Lexical error: encountered "u" (117), after "\"Henkil\u00f6 pit\u00e4\u00e4 k\u00e4siss\u00e4\u00e4n esitett\u00e4, jossa lukee \\".
---end-message---
The failing expression:
==> image?eval [in template "43104#43145#2794039" at line 42, column 38]
----
FTL stack trace ("~" means nesting-related):
- Failed at: #assign imageJson = image?eval [in template "43104#43145#2794039" at line 42, column 17]
----
1<#assign
2 portlet_preferences = portletDisplay.getPortletPreferences()
3 portlet_use_custom_title = getterUtil.getString(portlet_preferences.getValue("portletSetupUseCustomTitle", ""))
4 portlet_title = htmlUtil.escape(portletDisplay.getTitle())
5 portletId = portletDisplay.instanceId
6 headingSize = 2
7/>
8
9<#assign scopeGroupTreePath = themeDisplay.getScopeGroup().treePath?split('/') />
10<#assign pirhaGroupId = scopeGroupTreePath[1]?number />
11
12<#assign images_folder = themeDisplay.getPathThemeImages() />
13<#assign pageUrl = "">
14
15<#if entries?has_content>
16 <#if portlet_use_custom_title == 'true' >
17 <h2 class="portlet-title-custom" id="heading-${portletId}">${portlet_title}</h2>
18 <#assign headingSize = 3 />
19 </#if>
20
21 <div class="pirha-news-list list-with-images grid-list">
22 <#list entries as curEntry>
23 <#assign assetRenderer = curEntry.getAssetRenderer() />
24 <#assign pageUrl = assetPublisherHelper.getAssetViewURL(renderRequest, renderResponse, assetRenderer, entry, true)>
25
26 <#assign article = assetRenderer.getArticle() />
27
28 <#assign title = webArticleHelper.getArticleFieldValue(article, "title") />
29 <#assign lead = webArticleHelper.getArticleFieldValue(article, "lead") />
30 <#assign content = webArticleHelper.getArticleFieldValue(article, "content") />
31 <#assign image = webArticleHelper.getArticleFieldValue(article, "mainImage") />
32 <#assign publishDate = curEntry.publishDate?string('d.M.yyyy') />
33 <#assign publishDateISO = curEntry.publishDate?string("yyyy-MM-dd") />
34
35 <#assign articleDDMStructure = article.getDDMStructure() />
36 <#assign typeCategories = webArticleHelper.getAssetCategoriesForArticleInVocabulary(article, pirhaGroupId, "Tyyppi") />
37
38 <#assign imageURL = "" />
39 <#assign imageAlt = '' />
40
41 <#if image?has_content && image != ''>
42 <#assign imageJson = image?eval />
43
44 <#if imageJson.url?? && imageJson.url != "">
45 <#assign imageURL = imageJson.url />
46 </#if>
47
48 <#if imageJson.alt?? && imageJson.alt != "">
49 <#assign imageAlt = imageJson.alt />
50 </#if>
51
52 <#if imageJson.fileEntryId?? && imageJson.fileEntryId != "">
53 <#assign fileEntryId = imageJson.fileEntryId />
54 </#if>
55 </#if>
56
57 <div class="pirha-news-list-item mb-4" data-article-id="${article.id}">
58 <div class="list-item-image ratio ratio-16x9">
59 <img src="${imageURL}" alt="${imageAlt}" class="image-cover" />
60 </div>
61
62 <h${headingSize} class="pirha-news-list--heading"><a href="${pageUrl}">${title}</a></h${headingSize}>
63
64 <div class="meta">
65 <#if typeCategories?has_content>
66 <#list typeCategories as category>
67 <#if category.getTitle('fi_FI') == 'Uutinen'>
68 <#assign categoryColor = 'color--orange-dark' />
69 <#elseif category.getTitle('fi_FI') == 'Blogi'>
70 <#assign categoryColor = 'color--violet' />
71 <#else>
72 <#assign categoryColor = 'color--blue' />
73 </#if>
74
75 <span class="meta-text category ${categoryColor}">${category.getTitle(locale)}</span>
76 </#list>
77 </#if>
78
79 <span data-date="${publishDateISO}">${publishDate}</span>
80 </div>
81 </div>
82 </#list>
83 </div>
84</#if>
85
86<@liferay_util["body-bottom"] outputKey="bodybottomNewsList">
87<script>
88 document.addEventListener("DOMContentLoaded", (event) => {
89 const listOne = document.querySelector('.pirha-bg-row .pirha-news-list.list-with-images');
90 const listTwo = document.querySelector('.pirha-bg-row .pirha-news-list.list-simple');
91
92 <#-- // Find the parent portlet columns -->
93 const columnOne = listOne.closest('.portlet-column');
94 const columnTwo = listTwo.closest('.portlet-column');
95
96 if (!columnOne || !columnTwo) {
97 return;
98 }
99
100 <#-- // Check if columns are adjacent -->
101 const columns = Array.from(document.querySelectorAll('.pirha-bg-row .portlet-column'));
102 const indexOne = columns.indexOf(columnOne);
103 const indexTwo = columns.indexOf(columnTwo);
104
105 <#-- // Only run if columns are adjacent -->
106 if (Math.abs(indexOne - indexTwo) !== 1) {
107 return;
108 }
109
110 <#-- // Ignore first two -->
111 const listOneItems = Array.from(listOne.querySelectorAll('.pirha-news-list-item')).slice(2);
112
113 function getListTwoItems() {
114 return Array.from(listTwo.querySelectorAll('.item'));
115 }
116
117 <#-- // Helper to get date timestamp -->
118 function getDate(item) {
119 const el = item.querySelector('.meta span[data-date]');
120 return el ? new Date(el.dataset.date).getTime() : 0;
121 }
122
123 <#-- // Helper to get title text -->
124 function getTitle(item) {
125 const el = item.querySelector('.heading a, .pirha-news-list--heading a');
126 return el ? el.textContent.trim().toLowerCase() : '';
127 }
128
129 <#-- // Comparator: date DESC, title ASC -->
130 function compareItems(a, b) {
131 const dateDiff = getDate(b) - getDate(a);
132 if (dateDiff !== 0) return dateDiff;
133 return getTitle(a).localeCompare(getTitle(b));
134 }
135
136 <#-- // Clone and insert for mobile -->
137 listOneItems.forEach(item => {
138 const title = getTitle(item);
139
140 <#-- // Mark original item as mobile-hidden -->
141 item.classList.add('item--mobile-hidden');
142
143 <#-- // Clone and mark as desktop-hidden (only visible on mobile) -->
144 const clone = item.cloneNode(true);
145 clone.classList.add('item--desktop-hidden');
146
147 <#-- // Remove all images from clone -->
148 clone.querySelectorAll('.list-item-image').forEach(img => img.remove());
149
150 <#-- // Find correct insert position -->
151 const insertBefore = getListTwoItems().find(existing => compareItems(clone, existing) < 0);
152
153 if (insertBefore) {
154 listTwo.insertBefore(clone, insertBefore);
155 } else {
156 listTwo.appendChild(clone);
157 }
158 });
159 })
160</script>
161
162<style>
163 .item--mobile-hidden {
164 display: none;
165 }
166
167 .item--desktop-hidden {
168 display: none;
169 }
170
171 <#-- /* Mobile view */ -->
172 @media (max-width: 767px) {
173 .item--mobile-hidden {
174 display: none !important;
175 }
176
177 .item--desktop-hidden {
178 display: block !important;
179 }
180 }
181
182 <#-- /* Desktop view */ -->
183 @media (min-width: 768px) {
184 .item--mobile-hidden {
185 display: block !important;
186 }
187
188 .item--desktop-hidden {
189 display: none !important;
190 }
191 }
192</style>
193</@>
Päivitetty 21.3.2025