News Releases
An error occurred while processing the template.
The following has evaluated to null or missing:
==> rootElement.selectSingleNode("//dynamic-element[@field-reference='${fieldName}']/dynamic-content") [in template "20116#20152#608821196" at line 82, column 41]
----
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 node = rootElement.selectSing... [in template "20116#20152#608821196" in function "getFieldValue" at line 82, column 25]
----
1<#assign serviceContext = staticUtil["com.liferay.portal.kernel.service.ServiceContextThreadLocal"].getServiceContext()>
2<#assign themeDisplay = serviceContext.getThemeDisplay() />
3<#assign layout = themeDisplay['plid'] />
4<#assign layoutService = serviceLocator.findService("com.liferay.portal.kernel.service.LayoutLocalService")>
5<#assign layoutObject = layoutService.getLayout(layout?number) />
6<#assign page_group = layoutObject.getGroup() />
7<#assign site_title = page_group.getDescriptiveName() />
8
9
10 <style>
11 .imgThumbnail{
12 max-width: 300px;
13 height: auto;
14 }
15 <#--
16 CDPHP Style for CDPHP News Articles (Can be removed after Theme Modification )
17 -->
18 .cdphp .theme .banner-image__heading {
19 line-height: 1.2;
20 }
21 .cdphp .theme .card {
22 border-top-color: rgba(0, 0, 0, 0.125);
23 border-radius: 4px;
24 border-width: 0.0625rem;
25 }
26 .cdphp .theme .card a {
27 font-size: 2rem;
28 }
29 .cdphp .theme .bg-secondary {
30 background-color: #f0ebd8 !important;
31}
32 </style>
33
34<div class="row search-filter-browse__form bg-white">
35 <#if site_title?contains("Excellus")>
36 <h2>Select by Category</h2>
37 <div class="col-12 mb-4">
38 <div class="d-flex flex-column flex-md-row justify-content-around">
39 <button class="btn btn-secondary mx-auto mb-2 w-50 filter-btn" data-filter="Affordability">Affordability</button>
40 <button class="btn btn-secondary mx-auto ml-sm-1 mb-2 w-50 filter-btn" data-filter="Community Impact">Community Impact</button>
41 <button class="btn btn-secondary mx-auto ml-sm-1 mb-2 w-50 filter-btn" data-filter="Company News">Company News</button>
42 <button class="btn btn-secondary mx-auto ml-sm-1 mb-2 w-50 filter-btn" data-filter="Working Here">Working Here</button>
43 <button class="btn btn-secondary mx-auto ml-sm-1 mb-2 w-50 filter-btn" data-filter="Your Health">Your Health</button>
44 <button class="btn btn-primary order-5 mx-auto ml-sm-1 mb-2 w-50" id="resetBtn">Reset</button>
45 </div>
46 </div>
47 </#if>
48 <div class="col-12 mb-3">
49 <input
50 type="text"
51 id="searchInput"
52 class="form-control"
53 placeholder="Search articles..."
54 />
55 </div>
56 <div class="col-12 mb-4">
57 <select id="dateFilter" class="form-control mx-auto">
58 <option value="">All Dates</option>
59 <option value="1m">Last Month</option>
60 <option value="3m">Last 3 Months</option>
61 <option value="6m">Last 6 Months</option>
62 <option value="cy">Current Year</option>
63 <option value="py">Prior Year</option>
64 </select>
65 </div>
66</div>
67<div class="row mt-4">
68 <div class="news-count" id="newsCount">
69 Showing ${entries?size} article(s)
70 </div>
71
72 <#list entries as entry>
73
74 <#assign assetRenderer = entry.getAssetRenderer() />
75 <#assign journalArticle = assetRenderer.getArticle() />
76
77 <#assign document = saxReaderUtil.read(journalArticle.getContentByLocale(locale)) />
78 <#assign rootElement = document.getRootElement() />
79
80 <#-- Field helper -->
81 <#function getFieldValue fieldName>
82 <#assign node = rootElement.selectSingleNode("//dynamic-element[@field-reference='${fieldName}']/dynamic-content") />
83 <#if node??>
84 <#return node.getText() />
85 </#if>
86 <#return "" />
87 </#function>
88
89 <#function getImageURL fieldName>
90 <#assign jsonString = getFieldValue(fieldName) />
91
92 <#if jsonString?has_content>
93 <#assign imgJSON = jsonFactoryUtil.createJSONObject(jsonString) />
94
95 <#assign uuid = imgJSON.getString("uuid") />
96 <#assign groupId = imgJSON.getString("groupId") />
97
98 <#if uuid?has_content && groupId?has_content>
99 <#return themeDisplay.getPortalURL() + "/documents/" + groupId + "/" + uuid />
100 </#if>
101 </#if>
102
103 <#return "" />
104 </#function>
105
106 <#-- Get values -->
107 <#assign headline = getFieldValue("Headline") />
108 <#assign subheadline = getFieldValue("Subheadline") />
109 <#assign body = getFieldValue("Body") />
110 <#assign dateline = getFieldValue("Dateline")?date.iso?string("MMMM d, yyyy")!"" />
111 <#assign imageURL = getImageURL("thumbnailImage") />
112 <#assign imgAlt = getFieldValue("thumbnailAlt")!"">
113
114 <#assign categories = entry.getCategories() />
115 <#assign categoryTitles = [] />
116
117 <#list categories as cat>
118 <#assign categoryTitles = categoryTitles + [cat.getTitle(locale)] />
119 </#list>
120
121
122
123 <div class="news-card w-100 mb-4 border p-3"
124 data-categories="${categoryTitles?join(',')}"
125 data-headline="${headline?lower_case}"
126 data-body="${htmlUtil.stripHtml(body)?lower_case}"
127 data-date="${dateline}"
128 style="width:100%; margin-bottom:24px; border:1px solid #ddd; padding:16px;">
129
130
131 <div class="card-content" style="display:flex;">
132
133 <#-- Thumbnail (always reserve space) -->
134 <div class="media my-auto mx-3">
135 <#if imageURL?has_content>
136 <img src="${imageURL}" class="mr-2 imgThumbnail d-none d-md-block" alt="${imgAlt}"/>
137
138 </#if>
139 </div>
140
141 <div class="card-body media-body px-auto py-auto px-md-3 py-md-4">
142
143 <#-- Headline + Dateline row -->
144 <div>
145
146 <#assign articleId = journalArticle.getArticleId() />
147 <#assign classPK = entry.getClassPK() />
148
149 <#assign articleURL = "/news/article?articleId=" + articleId + "&classPK=" + classPK />
150
151 <#if headline?has_content>
152 <h2 class="my-0">
153 <a href="${articleURL}">
154 ${headline}
155 </a>
156 </h2>
157 </#if>
158
159 </div>
160
161 <#-- Subheadline -->
162 <#if subheadline?has_content>
163 <p class="text-muted">${subheadline} | ${dateline}</p>
164 <#else>
165 <p class="text-muted">${dateline}</p>
166 </#if>
167
168 <#-- Body excerpt -->
169 <#if body?has_content>
170 <div style="margin-top:12px;">
171 <#assign cleanBody = htmlUtil.stripHtml(body) />
172
173 <#if cleanBody?length <= 250>
174 ${cleanBody}
175 <#else>
176 <#assign cut = cleanBody?substring(0, 250) />
177 <#assign lastSpace = cut?last_index_of(" ") />
178
179 <#if lastSpace != -1>
180 ${cut?substring(0, lastSpace)}...
181 <#else>
182 ${cut}...
183 </#if>
184 </#if>
185 </div>
186 </#if>
187
188 <#-- Categories to exclude -->
189 <#assign excludedCategories = [
190 "Excellus",
191 "Univera",
192 "CDPHP"
193 ] />
194
195
196 <#-- Categories as Bootstrap badges -->
197 <div class="categories mt-3">
198
199 <#assign categories = entry.getCategories() />
200
201 <#list categories as cat>
202 <#assign catTitle = cat.getTitle(locale) />
203
204 <#-- Skip excluded categories -->
205 <#if !excludedCategories?seq_contains(catTitle)>
206 <span class="badge badge-secondary p-2 mt-3">
207 ${catTitle}
208 </span>
209 </#if>
210 </#list>
211
212 </div>
213
214 </div>
215
216 </div>
217
218 </div>
219
220 </#list>
221</div>
222<div class="flex-container no-results" style="display:none;">
223 <div class="flex-item-center m-auto">
224 <p><strong>No records found</strong> Try a new search term, or reset your search.</p>
225 <p><strong>Suggestions:</strong></p>
226 <ul>
227 <li>Make sure all words are spelled correctly</li>
228 <li>Try different keywords</li>
229 <li>Try more generic keywords</li>
230 <li>Try fewer keywords</li>
231 </ul>
232 </div>
233</div>
234
235<script>
236 document.addEventListener("DOMContentLoaded", function () {
237
238 const buttons = document.querySelectorAll(".filter-btn");
239 const cards = document.querySelectorAll(".news-card");
240 const resetBtn = document.getElementById("resetBtn");
241 const dateDropdown = document.getElementById("dateFilter");
242 let activeDateFilter = null;
243 const countEl = document.getElementById("newsCount");
244 const searchInput = document.getElementById("searchInput");
245 const noResults = document.querySelector(".no-results");
246
247 let activeFilter = null;
248
249 function updateCount() {
250 let visibleCount = 0;
251
252 cards.forEach(card => {
253 if (card.style.display !== "none") {
254 visibleCount++;
255 }
256 });
257
258 countEl.textContent = "Showing " + visibleCount + " article(s)";
259
260 // Toggle "no results"
261 if (visibleCount === 0) {
262 noResults.style.display = "flex";
263 } else {
264 noResults.style.display = "none";
265 }
266 }
267
268 function applyFilters() {
269 const searchTerm = (searchInput.value || "").toLowerCase();
270
271 cards.forEach(card => {
272 const categories = card.getAttribute("data-categories") || "";
273 const headline = card.getAttribute("data-headline") || "";
274 const body = card.getAttribute("data-body") || "";
275 const date = card.getAttribute("data-date");
276
277 let matchesCategory = true;
278 let matchesSearch = true;
279 let matchesDate = true;
280
281 if (activeFilter) {
282 matchesCategory = categories.includes(activeFilter);
283 }
284
285 if (searchTerm) {
286 matchesSearch =
287 headline.includes(searchTerm) ||
288 body.includes(searchTerm);
289 }
290
291 if (activeDateFilter) {
292 matchesDate = isWithinRange(date, activeDateFilter);
293 }
294
295 if (matchesCategory && matchesSearch && matchesDate) {
296 card.style.display = "block";
297 } else {
298 card.style.display = "none";
299 }
300 });
301
302 updateCount();
303 }
304
305 // Category buttons
306 buttons.forEach(button => {
307 button.addEventListener("click", function () {
308 activeFilter = this.getAttribute("data-filter");
309
310 buttons.forEach(btn => btn.classList.remove("active"));
311 this.classList.add("active");
312
313 applyFilters();
314 });
315 });
316
317 // Search typing (live)
318 searchInput.addEventListener("input", function () {
319 applyFilters();
320 });
321
322 //Date Filter Event Listener
323 dateDropdown.addEventListener("change", function () {
324 activeDateFilter = this.value || null;
325 applyFilters();
326 });
327
328 //Date filtering
329 function isWithinRange(articleDateStr, range) {
330 if (!articleDateStr) return false;
331
332 const articleDate = new Date(articleDateStr);
333 const now = new Date();
334
335 let startDate = null;
336 let endDate = new Date(now);
337
338 switch (range) {
339 case "1m":
340 startDate = new Date();
341 startDate.setMonth(now.getMonth() - 1);
342 break;
343
344 case "3m":
345 startDate = new Date();
346 startDate.setMonth(now.getMonth() - 3);
347 break;
348
349 case "6m":
350 startDate = new Date();
351 startDate.setMonth(now.getMonth() - 6);
352 break;
353
354 case "cy":
355 startDate = new Date(now.getFullYear(), 0, 1);
356 endDate = new Date(now.getFullYear(), 11, 31);
357 break;
358
359 case "py":
360 startDate = new Date(now.getFullYear() - 1, 0, 1);
361 endDate = new Date(now.getFullYear() - 1, 11, 31);
362 break;
363 }
364
365 return articleDate >= startDate && articleDate <= endDate;
366 }
367
368 //Reset the page
369 resetBtn.addEventListener("click", function () {
370 window.location.reload();
371 });
372
373 });
374</script>