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