Manuals, Processes and Guidelines

An error occurred while processing the template.
The following has evaluated to null or missing:
==> types[0]  [in template "20157#20197#1588857" at line 70, column 40]

----
Tip: It's the final [] step 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 defaultType = types[0]  [in template "20157#20197#1588857" at line 70, column 17]
----
1<#assign dlFileEntryUtil=serviceLocator.findService("com.liferay.document.library.kernel.service.DLFileEntryLocalService")> 
2<#assign dlFileEntryClass="com.liferay.document.library.kernel.model.DLFileEntry"> 
3<#assign portletID="${themeDisplay.getPortletDisplay().getId()}"> 
4<#assign customTagID="ap_"> 
5<#assign currentUrl = themeDisplay.getURLCurrent()!""> 
6<#if currentUrl?matches(".*[?&]type=([^&]+).*")> 
7    <#assign urlType = "ap_" + currentUrl?matches(".*[?&]type=([^&]+).*")?groups[1]!""/> 
8<#else> 
9    <#assign urlType = "ap_"/> 
10</#if> 
11							 
12<!--Type of Document--> 
13<div class="container no-padding"> 
14    <div class="row"> 
15        <div class="col-md-12" style="padding: 0px;"> 
16            <#assign types=[]> 
17            <#assign categories={}> 
18            <#assign categoriesByType={}> 
19 
20            <#list entries as curEntry> 
21                <#if curEntry.getClassName()==dlFileEntryClass> 
22                    <#assign dlFileEntry=dlFileEntryUtil.getFileEntry(curEntry.getClassPK())> 
23                    <#assign entryTypes=curEntry.getTags()> 
24                    <#assign entryCategories=curEntry.getCategories()> 
25 
26                    <!-- Type --> 
27                    <#list entryTypes as type> 
28                        <#list type.name?split(",") as typeName> 
29                            <#if typeName?matches("^" + customTagID + ".*")> 
30                                <#assign typeTag=typeName?trim> 
31                                <#if !types?seq_contains(typeTag)> 
32                                    <#assign types=(types + [typeTag])> 
33                                </#if> 
34                            </#if> 
35                        </#list> 
36                    </#list> 
37 
38                    <!-- Category --> 
39                    <#list entryCategories as category> 
40                        <#list category.name?split(",") as categoryName> 
41							<#assign categoryTag=categoryName?trim>    
42							<#if !categoryTag?matches("^LH.*")> 
43                                <#if categoriesByType[typeTag]?? && !categoriesByType[typeTag]?seq_contains(categoryTag)> 
44                                    <#assign categoriesByType = categoriesByType + {typeTag: categoriesByType[typeTag] + [categoryTag]} /> 
45                                <#elseif !(categoriesByType[typeTag]??)> 
46                                    <#assign categoriesByType = categoriesByType + {typeTag: [categoryTag]} /> 
47                                </#if>  
48							    <#if (category.description?split(",")[0]?trim?length gt 0)> 
49                                    <#assign categoryDescription=category.description?split(",")[0]?trim?substring(132, category.description?length - 28)?trim> 
50                                <#else> 
51								    <#assign categoryDescription=categoryTag> 
52								</#if> 
53                                <#assign categories = categories + {categoryTag: categoryDescription} /> 
54                            </#if> 
55                        </#list> 
56                    </#list>                       
57                </#if> 
58            </#list> 
59 
60            <!-- Sort Categories --> 
61            <#assign sortedCategoriesByType = {}> 
62            <#list categoriesByType?keys?sort as typeTag> 
63                <#assign sortedCategories = categoriesByType[typeTag]?sort> 
64                <#assign sortedCategoriesByType = sortedCategoriesByType + {typeTag: sortedCategories} /> 
65            </#list> 
66			<#assign categoriesByType = sortedCategoriesByType> 
67 
68            <!-- Default Type and Category --> 
69            <#if urlType == 'ap_' || !types?seq_contains(urlType)> 
70                <#assign defaultType = types[0]> 
71            <#else> 
72                <#assign defaultType = urlType> 
73			</#if> 
74            <#assign defaultCategory=categoriesByType[defaultType]?first> 
75 
76            <!-- Type of Documents Filters --> 
77            <div class="type-buttons"> 
78                <#list types as type> 
79                    <#assign typeTitle = type?replace(customTagID, "")?replace("_", " ")?split(" ")?map(x -> (x == 'and')?string("and", x?cap_first))?join(" ")> 
80                    <#if type == defaultType> 
81                        <button class="type-btn active" id="typeBtn_${type}" data-type="${type}"> 
82                            ${typeTitle} 
83                        </button> 
84                    <#else> 
85                        <button class="type-btn" id="typeBtn_${type}" data-type="${type}"> 
86                            ${typeTitle} 
87                        </button> 
88                    </#if> 
89                </#list> 
90            </div>             
91        </div> 
92    </div> 
93</div> 
94 
95<!-- Type of Document Title --> 
96<div class="container"> 
97    <div class="row"> 
98        <a class="col-md-12 main-title type-title" id="type-title">${defaultType?replace(customTagID, "")?replace("_", " ")?split(" ")?map(x -> (x == 'and')?string("and", x?cap_first))?join(" ")} 
99        </a> 
100    </div> 
101</div> 
102 
103<!-- Category of Document || Documents --> 
104<div class="container"> 
105    <div class="row"> 
106        <div class="filters-title"> 
107            Filters 
108        </div> 
109        <!-- Category of Documents Filters --> 
110        <div class="col-md-3 category-filters" id="categories_${portletID}">             
111        <#list categoriesByType[defaultType] as category> 
112            <#assign categoryTitle = categories[category]> 
113            <label class="category-label custom-checkbox"> 
114                <#if category == defaultCategory>  
115                    <input class="category-btn" type="checkbox" value="${category}" checked> 
116                <#else> 
117                    <input class="category-btn" type="checkbox" value="${category}"> 
118                </#if> 
119                <span class="custom-checkmark"></span> 
120                <a class="category-text">${categoryTitle}</a> 
121            </label> 
122        </#list> 
123    </div> 
124        <!-- Documents --> 
125        <div class="col-md-7 multiple-documents-files" style="padding-bottom: 30px">   
126            <div class="tiles-container"> 
127                <#if entries?has_content> 
128                    <#list entries as curEntry> 
129                        <#if curEntry.getClassName()==dlFileEntryClass> 
130                            <#assign dlFileEntry=dlFileEntryUtil.getFileEntry(curEntry.getClassPK())> 
131                            <#assign entryTypes=curEntry.getTags()> 
132                            <#assign entryCategories=curEntry.getCategories()> 
133                            <#assign classTypes=""> 
134                            <#assign classCategories=""> 
135                            <#list entryTypes as type> 
136                                <#list type.name?split(",") as typeName> 
137                                    <#assign classTypes=classTypes + " " + typeName?trim> 
138                                </#list> 
139                            </#list> 
140                                <#list entryCategories as category> 
141                                <#list category.name?split(",") as categoryName> 
142                                    <#assign classCategories=classCategories + " " + categoryName?trim> 
143                                </#list> 
144                            </#list> 
145                            <div class="multiple-documents-tile small-box-shadow-containers ${portletID} ${classTypes} ${classCategories}"> 
146                                <div class="multiple-documents-tile-text">                                  
147                                    <#if (curEntry.getDescription(locale)?split(",")[0]?trim?length gt 0)> 
148                                        ${curEntry.getDescription(locale)} 
149                                    <#else> 
150								        ${curEntry.getTitle(locale)} 
151								    </#if> 
152                                </div> 
153                                <div class="multiple-documents-tile-download"> 
154                                    <a href="${curEntry.getAssetRenderer().getURLDownload(themeDisplay)}" target="_blank"> 
155                                        Download 
156                                    </a> 
157                                </div> 
158                            </div> 
159                        </#if> 
160                    </#list> 
161                </#if> 
162                <a class="anchor" id="${themeDisplay.getPortletDisplay().title}" name="${themeDisplay.getPortletDisplay().title}"></a> 
163            </div> 
164            <!--PAGINATION--> 
165            <div class="asset-pagination"> 
166                <div class="asset-pagination-showing"> 
167                    Showing <span id="startResults_${portletID}">1</span> to <span id="endResults_${portletID}">10</span> of <span id="totalResults_${portletID}">25</span> results 
168                </div> 
169                <div class="asset-pagination-btns"> 
170                    <button class="asset-pagination-btn" id="prevBtn_${portletID}"> <i class="fa-solid fa-arrow-left"></i> </button> 
171                    <button class="asset-pagination-btn active" id="firstBtn_${portletID}">1</button> 
172                    <button class="asset-pagination-btn" id="secondBtn_${portletID}">2</button> 
173                    <button class="asset-pagination-btn" id="thirdBtn_${portletID}">3</button> 
174                    <button class="asset-pagination-btn" id="fourthBtn_${portletID}">4</button> 
175                    <button class="asset-pagination-btn" id="nextBtn_${portletID}"><i class="fa-solid fa-arrow-right"></i></button> 
176                </div> 
177            </div> 
178        </div> 
179    </div> 
180</div> 
181 
182 
183<script> 
184(function() { 
185    var titleID = "${portletID}"; 
186    var currentType = "${defaultType}"; 
187    var currentCategory = "${defaultCategory}"; 
188    var itemsPerPage = isMobileDevice() ? 3 : 10; 
189    var currentPage = 0; 
190    var nextPage = 0; 
191    var taggedRows = []; 
192    var startPagination = 1; 
193    var rows = []; 
194    var totalTaggedRows = 0; 
195    var totalPages = 0; 
196    var nextStart = 0; 
197    var nextEnd = 0; 
198    var allCategoriesByType = { 
199        <#list categoriesByType as type, typeCategories> 
200            "${type}": [<#list typeCategories as category>"${category}"<#if category_has_next>,</#if></#list>] 
201            <#if type_has_next>,</#if> 
202        </#list> 
203    }; 
204    var allCategories = { 
205        <#list categories as categoryName, categoryDescription> 
206            "${categoryName}": "${categoryDescription}" 
207            <#if categoryName_has_next>,</#if> 
208        </#list> 
209    }; 
210 
211    function isMobileDevice() { 
212        const userAgent = navigator.userAgent || navigator.vendor || window.opera; 
213 
214        if (/android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(userAgent) ||  
215        ('ontouchstart' in window && (window.innerWidth <= 800 || window.innerHeight <= 600))) { 
216            return true; 
217
218 
219        return false; 
220    }    
221 
222    // Update Type 
223    function updateType(event) { 
224        var selectedType = event.target.getAttribute('data-type'); 
225        currentType = selectedType; 
226         
227        document.querySelectorAll('.type-btn').forEach(function(button) { 
228            button.classList.remove('active'); 
229        }); 
230        event.target.classList.add('active'); 
231 
232        var currentTypeTitle = currentType.replace("ap_", "").replace(/_/g, " ").split(" ")    .map(x => x.toLowerCase() === "and" ? "and" : x.charAt(0).toUpperCase() + x.slice(1)).join(" "); 
233        document.getElementById("type-title").text = currentTypeTitle; 
234 
235        updateCategories(currentType); 
236
237    
238    // Update Category based on type 
239    function updateCategories(type) { 
240        var categoryContainer = document.getElementById(`categories_${portletID}`); 
241        categoryContainer.innerHTML = ''; 
242 
243        var categories = allCategoriesByType[type] || []; 
244        categories.forEach(function(category) { 
245            var label = document.createElement("label"); 
246            label.classList.add("category-label", "custom-checkbox"); 
247 
248            var checkbox = document.createElement("input"); 
249            checkbox.type = 'checkbox'; 
250            checkbox.name = 'category'; 
251            checkbox.value = category; 
252            checkbox.classList.add("category-btn"); 
253 
254            if (category === categories[0]) { 
255                checkbox.checked = true; 
256                currentCategory = category; 
257
258 
259            var customCheckmark = document.createElement("span"); 
260            customCheckmark.classList.add("custom-checkmark"); 
261 
262            var categoryTitle = allCategories[category]; 
263            var categoryText = document.createElement("a"); 
264            categoryText.classList.add("category-text"); 
265            categoryText.textContent = categoryTitle; 
266 
267            label.appendChild(checkbox); 
268            label.appendChild(customCheckmark); 
269            label.appendChild(categoryText); 
270 
271            categoryContainer.appendChild(label); 
272        }); 
273 
274        document.querySelectorAll('.category-btn').forEach(function(checkbox) { 
275            checkbox.addEventListener('click', updateCategory); 
276        }); 
277 
278        updateRows(currentCategory); 
279
280 
281    function updateCategory(event) { 
282        var selectedCategory = event.target.getAttribute('value'); 
283        currentCategory = selectedCategory; 
284         
285        document.querySelectorAll('.category-btn').forEach(function(button) { 
286            button.checked = false; 
287        }); 
288        event.target.checked = true; 
289        updateRows(currentCategory); 
290
291    
292 
293     // Update Rows based on Type and Category 
294    function updateRows(category) { 
295        currentCategory = category; 
296         
297        rows = document.querySelectorAll(".multiple-documents-tile." + titleID); 
298        taggedRows = []; 
299        rows.forEach(function(row) { 
300            var tags = Array.from(row.classList); 
301            var categories = Array.from(row.classList); 
302 
303            // Check if row matches selected type and category 
304            if (tags.includes(currentType) && categories.includes(currentCategory)) { 
305                taggedRows.push(row); 
306
307 
308            // Hide all -> pagination will show 
309            if (!row.classList.contains("assetHidden")) { 
310                row.classList.add("assetHidden"); 
311
312        }); 
313        handlePaginationChange("0"); 
314
315 
316    //EVENT LISTENERS // 
317    // On load 
318    function initialHandle() { 
319        updateCategories("${defaultType}"); 
320
321    window.onload = initialHandle(); 
322    // Type 
323    document.querySelectorAll('.type-btn').forEach(function(button) { 
324        button.addEventListener('click', updateType); 
325    }); 
326    // Category 
327     document.addEventListener('DOMContentLoaded', function() { 
328        document.querySelectorAll('.category-btn').forEach(function(checkbox) { 
329            checkbox.addEventListener('click', updateCategory); 
330        }); 
331    }); 
332    // 
333 
334    //Pagination 
335    function handlePaginationChange(page) { 
336        totalTaggedRows = taggedRows.length; 
337        totalPages = Math.ceil((totalTaggedRows / itemsPerPage)); 
338        nextPage = 0; 
339        // defines what is the next page 
340        if (page === 'prev') { 
341            nextPage = currentPage - 1 < 0 ? currentPage : currentPage - 1; 
342            if (currentPage == nextPage) return; 
343        } else if (page === 'next') { 
344            nextPage = currentPage + 1 < totalPages ? currentPage + 1 : currentPage; 
345            if (currentPage == nextPage) return; 
346        } else { 
347            nextPage = parseInt(page, 10) + (startPagination - 1); 
348
349        nextStart = (nextPage) * itemsPerPage; 
350        nextEnd = nextStart + itemsPerPage; 
351        //hides current reports and shows reports for the next page 
352        handlePaginationReports(); 
353        //changes numbers in Showing x to y of z results 
354        handleShowingResults(); 
355        //updates the pagination labels 
356        handlePaginationButtons(); 
357        //update current page 
358        currentPage = nextPage; 
359
360 
361    function handlePaginationReports() { 
362        //hides current page reports 
363        var curStart = (currentPage) * itemsPerPage; 
364        var curEnd = curStart + itemsPerPage; 
365        for (let r = curStart; r < curEnd && r < totalTaggedRows; r++) { 
366            if (!taggedRows[r].classList.contains("assetHidden")) { 
367                taggedRows[r].classList.add("assetHidden"); 
368
369
370        //shows next page reports 
371        var nextStart = (nextPage) * itemsPerPage; 
372        var nextEnd = nextStart + itemsPerPage; 
373        for (let r = nextStart; r < nextEnd && r < totalTaggedRows; r++) { 
374            if (taggedRows[r].classList.contains("assetHidden")) { 
375                taggedRows[r].classList.remove("assetHidden"); 
376
377
378
379 
380    function handleShowingResults() { 
381        const startResultsLabel = document.getElementById('startResults_${portletID}'); 
382        startResultsLabel.innerText = nextStart + 1; 
383        const endResultsLabel = document.getElementById('endResults_${portletID}'); 
384        endResultsLabel.innerText = nextEnd < totalTaggedRows ? nextEnd : totalTaggedRows; 
385        const totalResultsLabel = document.getElementById('totalResults_${portletID}'); 
386        totalResultsLabel.innerText = totalTaggedRows; 
387
388 
389    function handlePaginationButtons() { 
390        var currentPageNumber = nextPage + 1; 
391        // numbered buttons 
392        const firstBtn = document.getElementById('firstBtn_${portletID}'); 
393        const secondBtn = document.getElementById('secondBtn_${portletID}'); 
394        const thirdBtn = document.getElementById('thirdBtn_${portletID}'); 
395        const fourthBtn = document.getElementById('fourthBtn_${portletID}'); 
396        if (totalPages <= 4 || currentPageNumber == 1 || currentPageNumber == 2 || currentPageNumber == 3) { 
397            updateButton(firstBtn, currentPageNumber, 1); 
398            updateButton(secondBtn, currentPageNumber, 2); 
399            updateButton(thirdBtn, currentPageNumber, 3); 
400            updateButton(fourthBtn, currentPageNumber, 4); 
401            startPagination = 1; 
402        } else if (currentPageNumber == totalPages) { 
403            updateButton(firstBtn, currentPageNumber, currentPageNumber - 3); 
404            updateButton(secondBtn, currentPageNumber, currentPageNumber - 2); 
405            updateButton(thirdBtn, currentPageNumber, currentPageNumber - 1); 
406            updateButton(fourthBtn, currentPageNumber, currentPageNumber); 
407            startPagination = currentPageNumber - 3; 
408        } else if (currentPageNumber == totalPages - 1) { 
409            updateButton(firstBtn, currentPageNumber, currentPageNumber - 2); 
410            updateButton(secondBtn, currentPageNumber, currentPageNumber - 1); 
411            updateButton(thirdBtn, currentPageNumber, currentPageNumber); 
412            updateButton(fourthBtn, currentPageNumber, currentPageNumber + 1); 
413            startPagination = currentPageNumber - 2; 
414        } else if (currentPageNumber > 3) { 
415            updateButton(firstBtn, currentPageNumber, currentPageNumber - 2); 
416            updateButton(secondBtn, currentPageNumber, currentPageNumber - 1); 
417            updateButton(thirdBtn, currentPageNumber, currentPageNumber); 
418            updateButton(fourthBtn, currentPageNumber, currentPageNumber + 1); 
419            startPagination = currentPageNumber - 2; 
420
421        // prev and next buttons 
422        const prevBtn = document.getElementById('prevBtn_${portletID}'); 
423        const nextBtn = document.getElementById('nextBtn_${portletID}'); 
424        if (currentPageNumber == 1) { 
425            prevBtn.classList.add('disabled'); 
426        } else { 
427            prevBtn.classList.remove('disabled'); 
428
429        if (currentPageNumber == totalPages) { 
430            nextBtn.classList.add('disabled'); 
431        } else { 
432            nextBtn.classList.remove('disabled'); 
433
434
435 
436    function updateButton(button, currentPageNumber, label) { 
437        // active 
438        if (currentPageNumber == label) { 
439            button.classList.add('active'); 
440        } else { 
441            button.classList.remove('active'); 
442
443        // label + visibility 
444        if (totalPages >= label && currentPageNumber <= totalPages) { 
445            button.classList.remove('assetHidden'); 
446            button.innerText = label; 
447        } else { 
448            button.classList.add('assetHidden'); 
449
450
451    // 
452    //EVENT LISTENERS // 
453    // Pagination event listener 
454    var prevBtn = document.getElementById(`prevBtn_${portletID}`); 
455    prevBtn.addEventListener('click', function(event) { 
456        handlePaginationChange("prev"); 
457    }); 
458    var firstBtn = document.getElementById(`firstBtn_${portletID}`); 
459    firstBtn.addEventListener('click', function(event) { 
460        handlePaginationChange("0"); 
461    }); 
462    var secondBtn = document.getElementById(`secondBtn_${portletID}`); 
463    secondBtn.addEventListener('click', function(event) { 
464        handlePaginationChange("1"); 
465    }); 
466    var thirdBtn = document.getElementById(`thirdBtn_${portletID}`); 
467    thirdBtn.addEventListener('click', function(event) { 
468        handlePaginationChange("2"); 
469    }); 
470    var fourthBtn = document.getElementById(`fourthBtn_${portletID}`); 
471    fourthBtn.addEventListener('click', function(event) { 
472        handlePaginationChange("3"); 
473    }); 
474    var nextBtn = document.getElementById(`nextBtn_${portletID}`); 
475    nextBtn.addEventListener('click', function(event) { 
476        handlePaginationChange("next"); 
477    }); 
478    /////////////////////// 
479})(); 
480</script>