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\TypoScript\TypoScriptService; 19use TYPO3\CMS\Core\Utility\GeneralUtility; 20 21/** 22 * Extension class creating text based menus 23 */ 24class TextMenuContentObject extends AbstractMenuContentObject 25{ 26 /** 27 * Calls processItemStates() so that the common configuration for the menu items are resolved into individual configuration per item. 28 * Sets the result for the new "normal state" in $this->result 29 * 30 * @see AbstractMenuContentObject::processItemStates() 31 */ 32 public function generate() 33 { 34 $itemConfiguration = []; 35 $splitCount = count($this->menuArr); 36 if ($splitCount) { 37 $itemConfiguration = $this->processItemStates($splitCount); 38 } 39 if (!empty($this->mconf['debugItemConf'])) { 40 echo '<h3>$itemConfiguration:</h3>'; 41 debug($itemConfiguration); 42 } 43 $this->result = $itemConfiguration; 44 } 45 46 /** 47 * Traverses the ->result array of menu items configuration (made by ->generate()) and renders each item. 48 * During the execution of this function many internal methods prefixed "extProc_" from this class is called and 49 * many of these are for now dummy functions. 50 * An instance of ContentObjectRenderer is also made and for each menu item rendered it is loaded with 51 * the record for that page so that any stdWrap properties that applies will have the current menu items record available. 52 * 53 * @return string The HTML for the menu (returns result through $this->extProc_finish(); ) 54 */ 55 public function writeMenu() 56 { 57 if (empty($this->result)) { 58 return ''; 59 } 60 61 $this->WMresult = ''; 62 $this->WMmenuItems = count($this->result); 63 $typoScriptService = GeneralUtility::makeInstance(TypoScriptService::class); 64 $this->WMsubmenuObjSuffixes = $typoScriptService->explodeConfigurationForOptionSplit(['sOSuffix' => $this->mconf['submenuObjSuffixes'] ?? null], $this->WMmenuItems); 65 foreach ($this->result as $key => $val) { 66 $GLOBALS['TSFE']->register['count_HMENU_MENUOBJ']++; 67 $GLOBALS['TSFE']->register['count_MENUOBJ']++; 68 // Initialize the cObj with the page record of the menu item 69 $this->WMcObj->start($this->menuArr[$key], 'pages', $this->request); 70 $this->I = []; 71 $this->I['key'] = $key; 72 $this->I['val'] = $val; 73 $this->I['title'] = $this->getPageTitle(($this->menuArr[$key]['title'] ?? ''), ($this->menuArr[$key]['nav_title'] ?? '')); 74 $this->I['title.'] = $this->I['val']['stdWrap.'] ?? []; 75 $this->I['title'] = $this->WMcObj->stdWrapValue('title', $this->I ?? []); 76 $this->I['uid'] = $this->menuArr[$key]['uid'] ?? 0; 77 $this->I['mount_pid'] = $this->menuArr[$key]['mount_pid'] ?? 0; 78 $this->I['pid'] = $this->menuArr[$key]['pid'] ?? 0; 79 $this->I['spacer'] = $this->menuArr[$key]['isSpacer']; 80 // Set access key 81 if ($this->mconf['accessKey'] ?? false) { 82 $this->I['accessKey'] = $this->accessKey((string)($this->I['title'] ?? '')); 83 } else { 84 $this->I['accessKey'] = []; 85 } 86 // Make link tag 87 $this->I['val']['ATagParams'] = $this->WMcObj->getATagParams($this->I['val']); 88 $this->I['val']['additionalParams'] = $this->WMcObj->stdWrapValue('additionalParams', $this->I['val']); 89 $this->I['linkHREF'] = $this->link((int)$key, (string)($this->I['val']['altTarget'] ?? ''), ($this->mconf['forceTypeValue'] ?? '')); 90 if (empty($this->I['linkHREF'])) { 91 $this->I['val']['doNotLinkIt'] = 1; 92 } 93 // Title attribute of links: 94 $titleAttrValue = $this->WMcObj->stdWrapValue('ATagTitle', $this->I['val']); 95 $titleAttrValue .= $this->I['accessKey']['alt'] ?? ''; 96 if ($titleAttrValue !== '') { 97 $this->I['linkHREF']['title'] = $titleAttrValue; 98 } 99 100 // stdWrap for doNotLinkIt 101 $this->I['val']['doNotLinkIt'] = $this->WMcObj->stdWrapValue('doNotLinkIt', $this->I['val']); 102 // Compile link tag 103 if (!$this->I['val']['doNotLinkIt']) { 104 $this->I['val']['doNotLinkIt'] = 0; 105 } 106 if (!$this->I['spacer'] && $this->I['val']['doNotLinkIt'] != 1) { 107 $this->setATagParts(); 108 } else { 109 $this->I['A1'] = ''; 110 $this->I['A2'] = ''; 111 } 112 // ATagBeforeWrap processing: 113 if ($this->I['val']['ATagBeforeWrap'] ?? false) { 114 $wrapPartsBefore = explode('|', $this->I['val']['linkWrap'] ?? ''); 115 $wrapPartsAfter = ['', '']; 116 } else { 117 $wrapPartsBefore = ['', '']; 118 $wrapPartsAfter = explode('|', $this->I['val']['linkWrap'] ?? ''); 119 } 120 if (($this->I['val']['stdWrap2'] ?? false) || isset($this->I['val']['stdWrap2.'])) { 121 $stdWrap2 = (string)(isset($this->I['val']['stdWrap2.']) ? $this->WMcObj->stdWrap('|', $this->I['val']['stdWrap2.']) : '|'); 122 $wrapPartsStdWrap = explode($this->I['val']['stdWrap2'] ?: '|', $stdWrap2); 123 } else { 124 $wrapPartsStdWrap = ['', '']; 125 } 126 // Make before, middle and after parts 127 $this->I['parts'] = []; 128 $this->I['parts']['before'] = $this->getBeforeAfter('before'); 129 $this->I['parts']['stdWrap2_begin'] = $wrapPartsStdWrap[0]; 130 // stdWrap for doNotShowLink 131 $this->I['val']['doNotShowLink'] = $this->WMcObj->stdWrapValue('doNotShowLink', $this->I['val']); 132 if (!$this->I['val']['doNotShowLink']) { 133 $this->I['parts']['notATagBeforeWrap_begin'] = $wrapPartsAfter[0] ?? ''; 134 $this->I['parts']['ATag_begin'] = $this->I['A1']; 135 $this->I['parts']['ATagBeforeWrap_begin'] = $wrapPartsBefore[0] ?? ''; 136 $this->I['parts']['title'] = $this->I['title']; 137 $this->I['parts']['ATagBeforeWrap_end'] = $wrapPartsBefore[1] ?? ''; 138 $this->I['parts']['ATag_end'] = $this->I['A2']; 139 $this->I['parts']['notATagBeforeWrap_end'] = $wrapPartsAfter[1] ?? ''; 140 } 141 $this->I['parts']['stdWrap2_end'] = $wrapPartsStdWrap[1]; 142 $this->I['parts']['after'] = $this->getBeforeAfter('after'); 143 // Passing I to a user function 144 if ($this->mconf['IProcFunc'] ?? false) { 145 $this->I = $this->userProcess('IProcFunc', $this->I); 146 } 147 // Merge parts + beforeAllWrap 148 $this->I['theItem'] = implode('', $this->I['parts']); 149 // allWrap: 150 $allWrap = $this->WMcObj->stdWrapValue('allWrap', $this->I['val']); 151 $this->I['theItem'] = $this->WMcObj->wrap($this->I['theItem'], $allWrap); 152 if ($this->I['val']['subst_elementUid'] ?? false) { 153 $this->I['theItem'] = str_replace('{elementUid}', (string)$this->I['uid'], $this->I['theItem']); 154 } 155 // allStdWrap: 156 if (is_array($this->I['val']['allStdWrap.'] ?? null)) { 157 $this->I['theItem'] = $this->WMcObj->stdWrap($this->I['theItem'], $this->I['val']['allStdWrap.']); 158 } 159 // Calling extra processing function 160 $this->extProc_afterLinking((int)$key); 161 } 162 return $this->extProc_finish(); 163 } 164 165 /** 166 * Generates the before* and after* stdWrap for TMENUs 167 * Evaluates: 168 * - before.stdWrap* 169 * - beforeWrap 170 * - after.stdWrap* 171 * - afterWrap 172 * 173 * @param string $pref Can be "before" or "after" and determines which kind of stdWrap to process (basically this is the prefix of the TypoScript properties that are read from the ->I['val'] array 174 * @return string The resulting HTML 175 */ 176 protected function getBeforeAfter($pref) 177 { 178 $processedPref = $this->WMcObj->stdWrapValue($pref, $this->I['val']); 179 if (isset($this->I['val'][$pref . 'Wrap'])) { 180 return $this->WMcObj->wrap($processedPref, $this->I['val'][$pref . 'Wrap']); 181 } 182 return $processedPref; 183 } 184 185 /** 186 * Called right after the creation of links for the menu item. This is also the last function call before the while-loop traversing menu items goes to the next item. 187 * This function MUST set $this->WMresult.=[HTML for menu item] to add the generated menu item to the internal accumulation of items. 188 * 189 * @param int $key Pointer to $this->menuArr[$key] where the current menu element record is found 190 * @see writeMenu() 191 */ 192 protected function extProc_afterLinking($key) 193 { 194 $explicitSpacerRenderingEnabled = ($this->mconf['SPC'] ?? false); 195 $isSpacerPage = $this->I['spacer'] ?? false; 196 // If rendering of SPACERs is enabled, also allow rendering submenus with Spacers 197 if (!$isSpacerPage || $explicitSpacerRenderingEnabled) { 198 // Add part to the accumulated result + fetch submenus 199 $this->I['theItem'] .= $this->subMenu($this->I['uid'], $this->WMsubmenuObjSuffixes[$key]['sOSuffix'] ?? ''); 200 } 201 $part = $this->WMcObj->stdWrapValue('wrapItemAndSub', $this->I['val']); 202 $this->WMresult .= $part ? $this->WMcObj->wrap($this->I['theItem'], $part) : $this->I['theItem']; 203 } 204 205 /** 206 * Called before the writeMenu() function returns (only if a menu was generated) 207 * 208 * @return string The total menu content should be returned by this function 209 * @see writeMenu() 210 */ 211 protected function extProc_finish() 212 { 213 if (is_array($this->mconf['stdWrap.'] ?? null)) { 214 $this->WMresult = (string)$this->WMcObj->stdWrap($this->WMresult, $this->mconf['stdWrap.']); 215 } 216 return $this->WMcObj->wrap($this->WMresult, $this->mconf['wrap'] ?? ''); 217 } 218} 219