1<?php 2 3/* 4 * This file is part of the TYPO3 CMS project. 5 * 6 * It is free software; you can redistribute it and/or modify it under 7 * the terms of the GNU General Public License, either version 2 8 * of the License, or any later version. 9 * 10 * For the full copyright and license information, please read the 11 * LICENSE.txt file that was distributed with this source code. 12 * 13 * The TYPO3 project - inspiring people to share! 14 */ 15 16namespace TYPO3\CMS\Frontend\ContentObject\Menu; 17 18use TYPO3\CMS\Core\Collection\AbstractRecordCollection; 19use TYPO3\CMS\Core\Utility\GeneralUtility; 20use TYPO3\CMS\Frontend\Category\Collection\CategoryCollection; 21 22/** 23 * Utility class for menus based on category collections of pages. 24 * 25 * Returns all the relevant pages for rendering with a menu content object. 26 * @internal this is only used for internal purposes and solely used for EXT:frontend and not part of TYPO3's Core API. 27 */ 28class CategoryMenuUtility 29{ 30 /** 31 * @var string Name of the field used for sorting the pages 32 */ 33 protected static $sortingField; 34 35 /** 36 * Collects all pages for the selected categories, sorted according to configuration. 37 * 38 * @param string $selectedCategories Comma-separated list of system categories primary keys 39 * @param array $configuration TypoScript configuration for the "special." keyword 40 * @param \TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject $parentObject Back-reference to the calling object 41 * @return array List of selected pages 42 */ 43 public function collectPages($selectedCategories, $configuration, $parentObject) 44 { 45 $selectedPages = []; 46 $categoriesPerPage = []; 47 // Determine the name of the relation field 48 $relationField = ''; 49 if (isset($configuration['relation.'])) { 50 $relationField = $parentObject->parent_cObj->stdWrap( 51 $configuration['relation'], 52 $configuration['relation.'] 53 ); 54 } elseif (isset($configuration['relation'])) { 55 $relationField = $configuration['relation']; 56 } 57 // Get the pages for each selected category 58 $selectedCategories = GeneralUtility::intExplode(',', $selectedCategories, true); 59 foreach ($selectedCategories as $aCategory) { 60 $collection = CategoryCollection::load( 61 $aCategory, 62 true, 63 'pages', 64 $relationField 65 ); 66 $categoryUid = 0; 67 if ($collection instanceof AbstractRecordCollection) { 68 $categoryUid = $collection->getUid(); 69 } 70 // Loop on the results, overlay each page record found 71 foreach ($collection as $pageItem) { 72 $parentObject->getSysPage()->versionOL('pages', $pageItem, true); 73 if (is_array($pageItem)) { 74 $selectedPages[$pageItem['uid']] = $parentObject->getSysPage()->getPageOverlay($pageItem); 75 // Keep a list of the categories each page belongs to 76 if (!isset($categoriesPerPage[$pageItem['uid']])) { 77 $categoriesPerPage[$pageItem['uid']] = []; 78 } 79 $categoriesPerPage[$pageItem['uid']][] = $categoryUid; 80 } 81 } 82 } 83 // Loop on the selected pages to add the categories they belong to, as comma-separated list of category uid's) 84 // (this makes them available for rendering, if needed) 85 foreach ($selectedPages as $uid => $pageRecord) { 86 $selectedPages[$uid]['_categories'] = implode(',', $categoriesPerPage[$uid]); 87 } 88 89 // Sort the pages according to the sorting property 90 self::$sortingField = isset($configuration['sorting.']) ? $parentObject->getParentContentObject()->stdWrap($configuration['sorting'], $configuration['sorting.']) : $configuration['sorting']; 91 $order = isset($configuration['order.']) ? $parentObject->getParentContentObject()->stdWrap($configuration['order'], $configuration['order.']) : $configuration['order']; 92 $selectedPages = $this->sortPages($selectedPages, $order); 93 94 return $selectedPages; 95 } 96 97 /** 98 * Sorts the selected pages 99 * 100 * If the sorting field is not defined or does not corresponding to an existing field 101 * of the "pages" tables, the list of pages will remain unchanged. 102 * 103 * @param array $pages List of selected pages 104 * @param string $order Order for sorting (should "asc" or "desc") 105 * @return array Sorted list of pages 106 */ 107 protected function sortPages($pages, $order) 108 { 109 // Perform the sorting only if a criterion was actually defined 110 if (!empty(self::$sortingField)) { 111 // Check that the sorting field exists (checking the first record is enough) 112 $firstPage = current($pages); 113 if (isset($firstPage[self::$sortingField])) { 114 // Make sure the order property is either "asc" or "desc" (default is "asc") 115 if (!empty($order)) { 116 $order = strtolower($order); 117 if ($order !== 'desc') { 118 $order = 'asc'; 119 } 120 } 121 uasort( 122 $pages, 123 [ 124 self::class, 125 'sortPagesUtility' 126 ] 127 ); 128 // If the sort order is descending, reverse the sorted array 129 if ($order === 'desc') { 130 $pages = array_reverse($pages, true); 131 } 132 } 133 } 134 return $pages; 135 } 136 137 /** 138 * Static utility for sorting pages according to the selected criterion 139 * 140 * @param array $pageA Record for first page to be compared 141 * @param array $pageB Record for second page to be compared 142 * @return int -1 if first argument is smaller than second argument, 1 if first is greater than second and 0 if both are equal 143 */ 144 public static function sortPagesUtility($pageA, $pageB) 145 { 146 return strnatcasecmp($pageA[self::$sortingField], $pageB[self::$sortingField]); 147 } 148} 149