All Training & Resources
tab-1
tab-2
An error occurred while processing the template.
The following has evaluated to null or missing:
==> productCategories.items [in template "49724393377863#35335#230212" at line 57, column 22]
----
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: categories = productCategories.items [in template "49724393377863#35335#230212" at line 57, column 9]
----
1<#assign
2 defaultImgMap = {
3 "Default": {
4 "image": "/documents/d/guest/default_logo.png",
5 "color": "gray"
6 },
7 "Livestream": {
8 "image": "/documents/d/guest/icon-desktop-svg",
9 "color": "purple"
10 },
11 "Recorded":{
12 "image": "/documents/d/guest/icon-play",
13 "color": "red"
14 },
15 "In Person":{
16 "image": "/documents/d/guest/icon-persons",
17 "color": "green"
18 },
19 "Download": {
20 "image": "/documents/d/guest/icon-download-black",
21 "color": "border-color"
22 },
23 "Self-paced": {
24 "image": "/documents/d/guest/icon-mouse-white",
25 "color": "black"
26 },
27 "Bank subscription": {
28 "image": "/documents/d/guest/icon-bank",
29 "color": "blue"
30 }
31 }
32/>
33
34<#assign commerceContext = renderRequest.getAttribute("COMMERCE_CONTEXT") />
35<#assign account = commerceContext.getAccountEntry() />
36<#assign accountId = account.getAccountEntryId() />
37<#assign chanelId = commerceContext.getCommerceChannelId() />
38
39<div class="product-card-tiles">
40 <#if entries?has_content>
41 <#list entries as curCPCatalogEntry>
42 <#assign
43 cpDefinitionId = curCPCatalogEntry.getCPDefinitionId()
44 productId = curCPCatalogEntry.getCProductId()
45 productName = curCPCatalogEntry.getName()
46 productShortDescription = curCPCatalogEntry.getShortDescription()
47 productDescription = curCPCatalogEntry.getDescription()
48 friendlyURL = cpContentHelper.getFriendlyURL(curCPCatalogEntry, themeDisplay)
49 defaultImageURL = cpContentHelper.getDefaultImageFileURL(accountId, cpDefinitionId)
50 defaultImageFileVersion = cpContentHelper.getCPDefinitionImageFileVersion(cpDefinitionId, request)
51 productDetail = restClient.get("/headless-commerce-delivery-catalog/v1.0/channels/${chanelId}/products/${productId}?accountId=${accountId}&nestedFields=categories,productSpecifications")
52 defaultImageURL = productDetail.urlImage?replace('https://localhost', '')
53 fileEntryIdAttr = 'data-fileentryid="' + defaultImageFileVersion.fileEntryId + '"'
54 specifications = productDetail.productSpecifications
55 customFields = productDetail.customFields
56 productCategories = restClient.get("/headless-commerce-delivery-catalog/v1.0/channels/${chanelId}/products/${productId}/categories?pageSize=75")
57 categories = productCategories.items
58 productsku= restClient.get("/headless-commerce-delivery-catalog/v1.0/channels/${chanelId}/products/${productId}/skus?pageSize=40")
59 erc = productDetail.externalReferenceCode
60 infoObj = {}
61 tags = productDetail.tags
62 featuredSpecificationKeys = ["fit", "weight", "material"]
63 eventTypeName = ""
64 startDate = ""
65 startTime = ""
66 endDate = ""
67 endTime = ""
68 deliverymethodArray = [{"id": "", "value": ""}]
69 locationAddress = ""
70 />
71 <#if categories?has_content>
72 <#list categories as category>
73 <#if category.vocabulary?replace(' ', '') == 'deliverymethod'>
74 <#assign deliverymethodArray += [{"id": category.id, "value": category.name}] />
75 <#else>
76 <#assign infoObj = infoObj + {category.vocabulary?replace(' ', ''): {"id": category.id, "value": category.name}} />
77 </#if>
78 </#list>
79 <#assign infoObj = infoObj + {"deliverymethod": deliverymethodArray} />
80 </#if>
81
82 <#list customFields as customField>
83 <#assign infoObj = infoObj + {(customField.name): customField.customValue.data} />
84 </#list>
85
86 <!-- <div class="d-none">
87 <ul>
88 <li>
89 <#assign sku = "" />
90 <#if cpContentHelper.getDefaultCPSku(curCPCatalogEntry)?has_content>
91 <#assign sku = cpContentHelper.getDefaultCPSku(curCPCatalogEntry).getSku() />
92 </#if>
93 SKU: ${sku}
94 </li>
95 <li>
96 <@liferay_commerce_ui["availability-label"] CPCatalogEntry=curCPCatalogEntry />
97 </li>
98
99 <#if categories?has_content>
100 <#list categories as category>
101 <#if category.vocabulary == 'event type'>
102 <#assign eventTypeName = category.name />
103 <#assign eventTypeId = category.id />
104 </#if>
105 <li>${category.vocabulary}: ${category.name}</li>
106 </#list>
107 </#if>
108 </ul>
109 </div> -->
110
111 <!-- Category Flags -->
112 <#assign
113 isInstitute = "false"
114 isSeminar = "false"
115 isLivestream = "false"
116 isOnlineCourse = "false"
117 isInPerson = "false"
118 isWebinar = "false"
119 isEvent = "false"
120 isPriceTypeFree = "false"
121 isPriceTypeSubs = "false"
122 isPriceTypeMB = "false"
123 start = ""
124 end = ""
125 startDateStr = ""
126 endDateStr = ""
127 startTime = ""
128 endTime = ""
129 year = ""
130 dateDisplay = ""
131 timeDisplay = ""
132 locationDisplay = ""
133 />
134
135 <#if categories?has_content>
136 <#list categories as category>
137 <#if category.vocabulary == 'price type' && category.name == "Free">
138 <#assign isPriceTypeFree = "true" />
139 </#if>
140 <#if category.vocabulary == 'price type' && category.name == "Subscription">
141 <#assign isPriceTypeSubs = "true" />
142 </#if>
143 <#if category.vocabulary == 'price type' && category.name == "Member Benefit">
144 <#assign isPriceTypeMB = "true" />
145 </#if>
146 <#if category.vocabulary == 'delivery method' && category.name == "In Person">
147 <#if infoObj["Event End Date"]?has_content>
148 <#assign end = infoObj["Event End Date"]?datetime("yyyy-MM-dd'T'HH:mm:ss'Z'") />
149 <#assign today = .now?datetime> <!-- Today's date without time -->
150 <#assign showInPerson = end < today />
151 <!-- Ensure 'end' is valid before performing the comparison -->
152 <#if !showInPerson>
153 <#assign isInPerson = "true" />
154 </#if>
155 </#if>
156 </#if>
157 <#if category.vocabulary == 'delivery method' && category.name == "Livestream">
158 <#if infoObj["Event End Date"]?has_content>
159 <#assign end = infoObj["Event End Date"]?datetime("yyyy-MM-dd'T'HH:mm:ss'Z'") />
160 <#assign today = .now?datetime> <!-- Today's date without time -->
161 <#assign showLiveStream = end < today />
162 <!-- Ensure 'end' is valid before performing the comparison -->
163 <#if !showLiveStream>
164 <#assign isLivestream = "true" />
165 </#if>
166 </#if>
167 </#if>
168 <#if category.vocabulary == 'event type' && category.name == "Institute">
169 <#assign isInstitute = "true" />
170 </#if>
171 <#if category.vocabulary == 'event type' && category.name == "Seminar">
172 <#assign isSeminar = "true" />
173 </#if>
174 <#if category.vocabulary == 'product type' && category.name == "Webinar">
175 <#assign isWebinar = "true" />
176 </#if>
177 <#if category.vocabulary == 'product type' && category.name == "Online Course">
178 <#assign isOnlineCourse = "true" />
179 </#if>
180 <#if category.vocabulary == 'product type' && category.name == "Event">
181 <#assign isEvent = "true" />
182 </#if>
183 <#if category.vocabulary == 'event type'>
184 <#assign eventTypeName = category.name />
185 </#if>
186 </#list>
187 </#if>
188
189 <div class="card education-card mb-4 asdasd">
190 <div class="card-body">
191 <div class="top-row">
192 <#if infoObj.deliverymethod??>
193 <div class="education-card-categories">
194 <#list infoObj.deliverymethod as format>
195 <#if format.value != "">
196 <#if defaultImgMap[format.value]?has_content>
197 <#if format.value == "Livestream">
198 <#if isLivestream == "true">
199 <span class="category">
200 <img src="${defaultImgMap[format.value]['image']}" alt="" class="category-img bg-${defaultImgMap[format.value]['color']}" />
201 <span class="category-name">${format.value}</span>
202 </span>
203 </#if>
204 <#elseif format.value == "In Person">
205 <#if isInPerson == "true">
206 <span class="category">
207 <img src="${defaultImgMap[format.value]['image']}" alt="" class="category-img bg-${defaultImgMap[format.value]['color']}" />
208 <span class="category-name">${format.value}</span>
209 </span>
210 </#if>
211 <#else>
212 <span class="category">
213 <img src="${defaultImgMap[format.value]['image']}" alt="" class="category-img bg-${defaultImgMap[format.value]['color']}" />
214 <span class="category-name">${format.value}</span>
215 </span>
216 </#if>
217 </#if>
218 </#if>
219 </#list>
220 </div>
221 </#if>
222
223 <#if isInstitute == "true">
224 <div class="certification-info">
225 <span class="certification-info-text">Earn Professional Certification</span>
226 <div class="certification-info-img"><i class="certification-info-img"></i></div>
227 </div>
228 </#if>
229 </div>
230
231 <div class="card-title">
232 <h3 class="header-s"><a href="${friendlyURL}">${productName}</a></h3>
233 </div>
234
235 <div class="education-card-details">
236 <#if infoObj["Event Start Date"]?has_content>
237 <#assign start = infoObj["Event Start Date"]?datetime("yyyy-MM-dd'T'HH:mm:ss'Z'") />
238 <#assign startDateStr = start?string("MMM d") />
239 <#assign year = start?string("yyyy") />
240 <#assign startTime = start?string("h:mm") + " " + start?string("a")?lower_case />
241 </#if>
242
243 <#if infoObj["Event End Date"]?has_content>
244 <#assign end = infoObj["Event End Date"]?datetime("yyyy-MM-dd'T'HH:mm:ss'Z'") />
245 <#assign endDateStr = end?string("MMM d") />
246 <#assign endTime = end?string("h:mm") + " " + end?string("a")?lower_case />
247 </#if>
248
249 <#if isLivestream == "true" || isInPerson == "true">
250 <#if start?has_content && end?has_content>
251 <#if start?string("yyyyMMdd") == end?string("yyyyMMdd")>
252 <#assign dateDisplay = startDateStr + ", " + year />
253 <#assign timeDisplay = startTime + "–" + endTime />
254 <#else>
255 <#assign dateDisplay = startDateStr + "–" + endDateStr + ", " + year />
256 <#assign timeDisplay = startTime />
257 </#if>
258 </#if>
259
260 </#if>
261 <div class="education-card-location">
262 <#if isWebinar == "true" || isEvent == "true">
263
264 <span class="date">${dateDisplay}</span>
265 <#if isWebinar == "true">
266 <#if isLivestream == "true" || isInPerson == "true">|</#if> <span class="category">Webinar</span>
267 </#if>
268
269 <#if isEvent == "true">
270 <#if isLivestream == "true" || isInPerson == "true">| </#if><span class="category">${eventTypeName}</span>
271 </#if>
272 <span class="time d-none">${timeDisplay}</span>
273 </#if>
274
275 <#if isInPerson == "true">
276 <span class="location">
277 <#if infoObj["Location City"]?has_content>
278 ${infoObj["Location City"]}
279 </#if>
280 <#if infoObj["Location State"]?has_content>
281 , ${infoObj["Location State"]}
282 </#if>
283 </span>
284 </#if>
285 </div>
286
287
288 <div class="education-card-credits">
289 <span class="credits-info"><#if infoObj["CPE Credits"]?has_content>CPE Credits: ${infoObj["CPE Credits"]}</#if></span>
290 </div>
291
292<!---Start ---->
293
294<!-- SKU Area -->
295<#assign productskuJson = jsonFactoryUtil.createJSONObject(productsku)/>
296<#if productskuJson?has_content && productskuJson.has("items")>
297 <#assign itemsArray = productskuJson.getJSONArray("items")/>
298 <#if itemsArray?has_content>
299<#assign priceList = [] />
300
301<#if itemsArray?has_content && itemsArray.length() gt 0>
302 <#list 0..itemsArray.length() - 1 as index>
303 <#assign item = itemsArray.getJSONObject(index) />
304 <#if item?has_content && item.getJSONArray("skuOptions")?has_content>
305 <#assign skuOptions = item.getJSONArray("skuOptions") />
306 <#assign registrationOption = "" />
307 <#assign deliveryMethod = "" />
308 <#assign rowdisplayName = "" />
309
310 <!-- Extract values from skuOptions -->
311 <#list 0..skuOptions.length() - 1 as i>
312 <#assign skuOption = skuOptions.getJSONObject(i) />
313 <#assign key = skuOption.getString("skuOptionKey") />
314 <#assign valueKey = skuOption.getString("skuOptionValueKey") />
315 <#assign valueName = skuOption.getJSONArray("skuOptionValueNames").getString(0) />
316
317 <#if key == "registration-options">
318 <#assign registrationOption = valueKey />
319 <#assign rowdisplayName = valueName />
320 </#if>
321
322 <#if key == "webinar-delivery-method">
323 <#assign deliveryMethod = valueKey />
324 </#if>
325 </#list>
326
327 <!-- Filter: Only show On-demand + desired registration-options -->
328 <#if isWebinar == "true">
329 <#if deliveryMethod == "ondemand" &&
330 (registrationOption == "icba-member" ||
331 registrationOption == "nonmember" ||
332 registrationOption == "unlimited-webinar-pass")>
333
334 <!-- Format price -->
335 <#assign price = item.price.priceFormatted />
336 <!-- Store display name + price in a map -->
337 <#assign priceList = priceList + [{"name": rowdisplayName, "price": price}] />
338 </#if>
339 <#else>
340 <#if registrationOption == "icba-member" ||
341 registrationOption == "nonmember" ||
342 registrationOption == "unlimited-webinar-pass">
343
344 <!-- Format price -->
345 <#assign price = item.price.priceFormatted />
346 <!-- Store display name + price in a map -->
347 <#assign priceList = priceList + [{"name": rowdisplayName, "price": price}] />
348 </#if>
349 </#if>
350 </#if>
351 </#list>
352 </#if>
353 </#if>
354 </#if>
355
356
357<!-- Display the collected name + price -->
358<div class="education-card-price">
359 <#if priceList?has_content>
360
361 <!-- First: Member -->
362 <#list priceList as item>
363 <#if item?has_content && item.name == "ICBA Member">
364 <span class="education-card-price-item member-price">Member: ${item.price}</span>
365 </#if>
366 </#list>
367
368 <!-- Second: Non-Member -->
369 <#list priceList as item>
370 <#if item?has_content && item.name == "Non-Member">
371 <span class="education-card-price-item member-price">${item.name}: ${item.price}</span>
372 </#if>
373 </#list>
374
375 <!-- Third: ICBA Bank Director Program Subscriber -->
376 <#list priceList as item>
377 <#if item?has_content && item.name == "Bank Director Program Subscriber">
378 <span class="education-card-price-item member-price">ICBA Bank Director Program Subscriber: ${item.price}</span>
379 </#if>
380 </#list>
381
382 <!-- Fourth: Unlimited Webinar Pass -->
383 <#list priceList as item>
384 <#if item?has_content && item.name == "Unlimited Webinar Pass">
385 <span class="education-card-price-item member-price">Unlimited Webinar Pass: ${item.price}</span>
386 </#if>
387 </#list>
388
389 </#if>
390</div>
391
392
393<!-----Ends --->
394 <div class="education-card-tags">
395 <#if categories?has_content>
396 <#assign topicCount = 0/>
397 <#assign displayedCategories = []/>
398 <#assign totalTopicCount = 0/>
399
400 <#-- First, count total topic categories -->
401 <#list categories as category>
402 <#if category.vocabulary == 'topic'>
403 <#assign totalTopicCount += 1/>
404 </#if>
405 </#list>
406
407 <#-- Now display first 3 topics -->
408 <#list categories as category>
409 <#if category.vocabulary == 'topic' && topicCount < 3>
410 <#assign taxonomyCategoryId = category.id/>
411 <#assign response = restClient.get("/headless-admin-taxonomy/v1.0/taxonomy-categories/${taxonomyCategoryId}") />
412 <#if response.taxonomyCategoryProperties?has_content>
413 <#assign firstProperty = response.taxonomyCategoryProperties[0] />
414 <#assign categoryValue = firstProperty.value />
415 <span class="tag-pill body-13">${categoryValue}</span>
416 <#else>
417 <span class="tag-pill body-13">${category.name}</span>
418 </#if>
419 <#assign topicCount += 1>
420 </#if>
421 </#list>
422
423 <#-- If more topics exist, show "+X More..." -->
424 <#if (totalTopicCount - topicCount) gt 0>
425 <span class="tag-pill body-13">+${totalTopicCount - topicCount} More...</span>
426 </#if>
427
428 </#if>
429 </div>
430 </div>
431 </div>
432 </div>
433 </#list>
434 </#if>
435</div>