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
|
|
|
|
16
|
|
|
namespace TYPO3\CMS\Frontend\ContentObject\Menu; |
17
|
|
|
|
18
|
|
|
use Psr\Http\Message\ServerRequestInterface; |
19
|
|
|
use TYPO3\CMS\Core\Cache\CacheManager; |
20
|
|
|
use TYPO3\CMS\Core\Context\Context; |
21
|
|
|
use TYPO3\CMS\Core\Context\LanguageAspect; |
22
|
|
|
use TYPO3\CMS\Core\Database\ConnectionPool; |
23
|
|
|
use TYPO3\CMS\Core\Database\RelationHandler; |
24
|
|
|
use TYPO3\CMS\Core\Domain\Repository\PageRepository; |
25
|
|
|
use TYPO3\CMS\Core\Page\AssetCollector; |
26
|
|
|
use TYPO3\CMS\Core\Site\Entity\Site; |
27
|
|
|
use TYPO3\CMS\Core\TimeTracker\TimeTracker; |
28
|
|
|
use TYPO3\CMS\Core\Type\Bitmask\PageTranslationVisibility; |
29
|
|
|
use TYPO3\CMS\Core\TypoScript\TemplateService; |
30
|
|
|
use TYPO3\CMS\Core\TypoScript\TypoScriptService; |
31
|
|
|
use TYPO3\CMS\Core\Utility\GeneralUtility; |
32
|
|
|
use TYPO3\CMS\Core\Utility\MathUtility; |
33
|
|
|
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer; |
34
|
|
|
use TYPO3\CMS\Frontend\ContentObject\Menu\Exception\NoSuchMenuTypeException; |
35
|
|
|
use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController; |
36
|
|
|
use TYPO3\CMS\Frontend\Typolink\PageLinkBuilder; |
37
|
|
|
|
38
|
|
|
/** |
39
|
|
|
* Generating navigation/menus from TypoScript |
40
|
|
|
* |
41
|
|
|
* The HMENU content object uses this (or more precisely one of the extension classes). |
42
|
|
|
* Among others the class generates an array of menu items. Thereafter functions from the subclasses are called. |
43
|
|
|
* The class is always used through extension classes like TextMenuContentObject. |
44
|
|
|
*/ |
45
|
|
|
abstract class AbstractMenuContentObject |
46
|
|
|
{ |
47
|
|
|
/** |
48
|
|
|
* tells you which menu number this is. This is important when getting data from the setup |
49
|
|
|
* |
50
|
|
|
* @var int |
51
|
|
|
*/ |
52
|
|
|
protected $menuNumber = 1; |
53
|
|
|
|
54
|
|
|
/** |
55
|
|
|
* 0 = rootFolder |
56
|
|
|
* |
57
|
|
|
* @var int |
58
|
|
|
*/ |
59
|
|
|
protected $entryLevel = 0; |
60
|
|
|
|
61
|
|
|
/** |
62
|
|
|
* Doktypes that define which should not be included in a menu |
63
|
|
|
* |
64
|
|
|
* @var int[] |
65
|
|
|
*/ |
66
|
|
|
protected $excludedDoktypes = [PageRepository::DOKTYPE_BE_USER_SECTION, PageRepository::DOKTYPE_SYSFOLDER]; |
67
|
|
|
|
68
|
|
|
/** |
69
|
|
|
* @var int[] |
70
|
|
|
*/ |
71
|
|
|
protected $alwaysActivePIDlist = []; |
72
|
|
|
|
73
|
|
|
/** |
74
|
|
|
* Loaded with the parent cObj-object when a new HMENU is made |
75
|
|
|
* |
76
|
|
|
* @var ContentObjectRenderer |
77
|
|
|
*/ |
78
|
|
|
public $parent_cObj; |
79
|
|
|
|
80
|
|
|
/** |
81
|
|
|
* accumulation of mount point data |
82
|
|
|
* |
83
|
|
|
* @var string[] |
84
|
|
|
*/ |
85
|
|
|
protected $MP_array = []; |
86
|
|
|
|
87
|
|
|
/** |
88
|
|
|
* HMENU configuration |
89
|
|
|
* |
90
|
|
|
* @var array |
91
|
|
|
*/ |
92
|
|
|
protected $conf = []; |
93
|
|
|
|
94
|
|
|
/** |
95
|
|
|
* xMENU configuration (TMENU etc) |
96
|
|
|
* |
97
|
|
|
* @var array |
98
|
|
|
*/ |
99
|
|
|
protected $mconf = []; |
100
|
|
|
|
101
|
|
|
/** |
102
|
|
|
* @var TemplateService |
103
|
|
|
*/ |
104
|
|
|
protected $tmpl; |
105
|
|
|
|
106
|
|
|
/** |
107
|
|
|
* @var PageRepository |
108
|
|
|
*/ |
109
|
|
|
protected $sys_page; |
110
|
|
|
|
111
|
|
|
/** |
112
|
|
|
* The base page-id of the menu. |
113
|
|
|
* |
114
|
|
|
* @var int |
115
|
|
|
*/ |
116
|
|
|
protected $id; |
117
|
|
|
|
118
|
|
|
/** |
119
|
|
|
* Holds the page uid of the NEXT page in the root line from the page pointed to by entryLevel; |
120
|
|
|
* Used to expand the menu automatically if in a certain root line. |
121
|
|
|
* |
122
|
|
|
* @var string |
123
|
|
|
*/ |
124
|
|
|
protected $nextActive; |
125
|
|
|
|
126
|
|
|
/** |
127
|
|
|
* The array of menuItems which is built |
128
|
|
|
* |
129
|
|
|
* @var array[] |
130
|
|
|
*/ |
131
|
|
|
protected $menuArr; |
132
|
|
|
|
133
|
|
|
/** |
134
|
|
|
* @var string |
135
|
|
|
*/ |
136
|
|
|
protected $hash; |
137
|
|
|
|
138
|
|
|
/** |
139
|
|
|
* @var array |
140
|
|
|
*/ |
141
|
|
|
protected $result = []; |
142
|
|
|
|
143
|
|
|
/** |
144
|
|
|
* Is filled with an array of page uid numbers + RL parameters which are in the current |
145
|
|
|
* root line (used to evaluate whether a menu item is in active state) |
146
|
|
|
* |
147
|
|
|
* @var array |
148
|
|
|
*/ |
149
|
|
|
protected $rL_uidRegister; |
150
|
|
|
|
151
|
|
|
/** |
152
|
|
|
* @var mixed[] |
153
|
|
|
*/ |
154
|
|
|
protected $I; |
155
|
|
|
|
156
|
|
|
/** |
157
|
|
|
* @var string |
158
|
|
|
*/ |
159
|
|
|
protected $WMresult; |
160
|
|
|
|
161
|
|
|
/** |
162
|
|
|
* @var int |
163
|
|
|
*/ |
164
|
|
|
protected $WMmenuItems; |
165
|
|
|
|
166
|
|
|
/** |
167
|
|
|
* @var array[] |
168
|
|
|
*/ |
169
|
|
|
protected $WMsubmenuObjSuffixes; |
170
|
|
|
|
171
|
|
|
/** |
172
|
|
|
* @var ContentObjectRenderer |
173
|
|
|
*/ |
174
|
|
|
protected $WMcObj; |
175
|
|
|
|
176
|
|
|
protected ?ServerRequestInterface $request = null; |
177
|
|
|
|
178
|
|
|
/** |
179
|
|
|
* Can be set to contain menu item arrays for sub-levels. |
180
|
|
|
* |
181
|
|
|
* @var array |
182
|
|
|
*/ |
183
|
|
|
protected $alternativeMenuTempArray = []; |
184
|
|
|
|
185
|
|
|
/** |
186
|
|
|
* Array key of the parentMenuItem in the parentMenuArr, if this menu is a subMenu. |
187
|
|
|
* |
188
|
|
|
* @var int|null |
189
|
|
|
*/ |
190
|
|
|
protected $parentMenuArrItemKey; |
191
|
|
|
|
192
|
|
|
/** |
193
|
|
|
* @var array |
194
|
|
|
*/ |
195
|
|
|
protected $parentMenuArr; |
196
|
|
|
|
197
|
|
|
protected const customItemStates = [ |
198
|
|
|
// IFSUB is TRUE if there exist submenu items to the current item |
199
|
|
|
'IFSUB', |
200
|
|
|
'ACT', |
201
|
|
|
// ACTIFSUB is TRUE if there exist submenu items to the current item and the current item is active |
202
|
|
|
'ACTIFSUB', |
203
|
|
|
// CUR is TRUE if the current page equals the item here! |
204
|
|
|
'CUR', |
205
|
|
|
// CURIFSUB is TRUE if there exist submenu items to the current item and the current page equals the item here! |
206
|
|
|
'CURIFSUB', |
207
|
|
|
'USR', |
208
|
|
|
'SPC', |
209
|
|
|
'USERDEF1', |
210
|
|
|
'USERDEF2' |
211
|
|
|
]; |
212
|
|
|
|
213
|
|
|
/** |
214
|
|
|
* The initialization of the object. This just sets some internal variables. |
215
|
|
|
* |
216
|
|
|
* @param TemplateService $tmpl The $this->getTypoScriptFrontendController()->tmpl object |
217
|
|
|
* @param PageRepository $sys_page The $this->getTypoScriptFrontendController()->sys_page object |
218
|
|
|
* @param int|string $id A starting point page id. This should probably be blank since the 'entryLevel' value will be used then. |
219
|
|
|
* @param array $conf The TypoScript configuration for the HMENU cObject |
220
|
|
|
* @param int $menuNumber Menu number; 1,2,3. Should probably be 1 |
221
|
|
|
* @param string $objSuffix Submenu Object suffix. This offers submenus a way to use alternative configuration for specific positions in the menu; By default "1 = TMENU" would use "1." for the TMENU configuration, but if this string is set to eg. "a" then "1a." would be used for configuration instead (while "1 = " is still used for the overall object definition of "TMENU") |
222
|
|
|
* @param ServerRequestInterface|null $request |
223
|
|
|
* @return bool Returns TRUE on success |
224
|
|
|
* @see \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer::HMENU() |
225
|
|
|
*/ |
226
|
|
|
public function start($tmpl, $sys_page, $id, $conf, $menuNumber, $objSuffix = '', ?ServerRequestInterface $request = null) |
227
|
|
|
{ |
228
|
|
|
$tsfe = $this->getTypoScriptFrontendController(); |
229
|
|
|
$this->conf = $conf; |
230
|
|
|
$this->menuNumber = $menuNumber; |
231
|
|
|
$this->mconf = $conf[$this->menuNumber . $objSuffix . '.']; |
232
|
|
|
$this->request = $request; |
233
|
|
|
$this->WMcObj = GeneralUtility::makeInstance(ContentObjectRenderer::class); |
234
|
|
|
// Sets the internal vars. $tmpl MUST be the template-object. $sys_page MUST be the PageRepository object |
235
|
|
|
if ($this->conf[$this->menuNumber . $objSuffix] && is_object($tmpl) && is_object($sys_page)) { |
236
|
|
|
$this->tmpl = $tmpl; |
237
|
|
|
$this->sys_page = $sys_page; |
238
|
|
|
// alwaysActivePIDlist initialized: |
239
|
|
|
$this->conf['alwaysActivePIDlist'] = (string)$this->parent_cObj->stdWrapValue('alwaysActivePIDlist', $this->conf ?? []); |
240
|
|
|
if (trim($this->conf['alwaysActivePIDlist'])) { |
241
|
|
|
$this->alwaysActivePIDlist = GeneralUtility::intExplode(',', $this->conf['alwaysActivePIDlist']); |
242
|
|
|
} |
243
|
|
|
// includeNotInMenu initialized: |
244
|
|
|
$this->conf['includeNotInMenu'] = $this->parent_cObj->stdWrapValue('includeNotInMenu', $this->conf, false); |
245
|
|
|
// exclude doktypes that should not be shown in menu (e.g. backend user section) |
246
|
|
|
if ($this->conf['excludeDoktypes'] ?? false) { |
247
|
|
|
$this->excludedDoktypes = GeneralUtility::intExplode(',', $this->conf['excludeDoktypes']); |
248
|
|
|
} |
249
|
|
|
// EntryLevel |
250
|
|
|
$this->entryLevel = $this->parent_cObj->getKey( |
251
|
|
|
$this->parent_cObj->stdWrapValue('entryLevel', $this->conf ?? []), |
|
|
|
|
252
|
|
|
$this->tmpl->rootLine |
253
|
|
|
); |
254
|
|
|
// Set parent page: If $id not stated with start() then the base-id will be found from rootLine[$this->entryLevel] |
255
|
|
|
// Called as the next level in a menu. It is assumed that $this->MP_array is set from parent menu. |
256
|
|
|
if ($id) { |
257
|
|
|
$this->id = (int)$id; |
258
|
|
|
} else { |
259
|
|
|
// This is a BRAND NEW menu, first level. So we take ID from rootline and also find MP_array (mount points) |
260
|
|
|
$this->id = (int)$this->tmpl->rootLine[$this->entryLevel]['uid']; |
261
|
|
|
// Traverse rootline to build MP_array of pages BEFORE the entryLevel |
262
|
|
|
// (MP var for ->id is picked up in the next part of the code...) |
263
|
|
|
foreach ($this->tmpl->rootLine as $entryLevel => $levelRec) { |
264
|
|
|
// For overlaid mount points, set the variable right now: |
265
|
|
|
if (($levelRec['_MP_PARAM'] ?? false) && ($levelRec['_MOUNT_OL'] ?? false)) { |
266
|
|
|
$this->MP_array[] = $levelRec['_MP_PARAM']; |
267
|
|
|
} |
268
|
|
|
// Break when entry level is reached: |
269
|
|
|
if ($entryLevel >= $this->entryLevel) { |
270
|
|
|
break; |
271
|
|
|
} |
272
|
|
|
// For normal mount points, set the variable for next level. |
273
|
|
|
if ($levelRec['_MP_PARAM'] && !$levelRec['_MOUNT_OL']) { |
274
|
|
|
$this->MP_array[] = $levelRec['_MP_PARAM']; |
275
|
|
|
} |
276
|
|
|
} |
277
|
|
|
} |
278
|
|
|
// Return FALSE if no page ID was set (thus no menu of subpages can be made). |
279
|
|
|
if ($this->id <= 0) { |
280
|
|
|
return false; |
281
|
|
|
} |
282
|
|
|
// Check if page is a mount point, and if so set id and MP_array |
283
|
|
|
// (basically this is ONLY for non-overlay mode, but in overlay mode an ID with a mount point should never reach this point anyways, so no harm done...) |
284
|
|
|
$mount_info = $this->sys_page->getMountPointInfo($this->id); |
285
|
|
|
if (is_array($mount_info)) { |
286
|
|
|
$this->MP_array[] = $mount_info['MPvar']; |
287
|
|
|
$this->id = $mount_info['mount_pid']; |
288
|
|
|
} |
289
|
|
|
// Gather list of page uids in root line (for "isActive" evaluation). Also adds the MP params in the path so Mount Points are respected. |
290
|
|
|
// (List is specific for this rootline, so it may be supplied from parent menus for speed...) |
291
|
|
|
if ($this->rL_uidRegister === null) { |
292
|
|
|
$this->rL_uidRegister = []; |
293
|
|
|
$rl_MParray = []; |
294
|
|
|
foreach ($this->tmpl->rootLine as $v_rl) { |
295
|
|
|
// For overlaid mount points, set the variable right now: |
296
|
|
|
if (($v_rl['_MP_PARAM'] ?? false) && ($v_rl['_MOUNT_OL'] ?? false)) { |
297
|
|
|
$rl_MParray[] = $v_rl['_MP_PARAM']; |
298
|
|
|
} |
299
|
|
|
// Add to register: |
300
|
|
|
$this->rL_uidRegister[] = 'ITEM:' . $v_rl['uid'] . |
301
|
|
|
( |
302
|
|
|
!empty($rl_MParray) |
303
|
|
|
? ':' . implode(',', $rl_MParray) |
304
|
|
|
: '' |
305
|
|
|
); |
306
|
|
|
// For normal mount points, set the variable for next level. |
307
|
|
|
if (($v_rl['_MP_PARAM'] ?? false) && !($v_rl['_MOUNT_OL'] ?? false)) { |
308
|
|
|
$rl_MParray[] = $v_rl['_MP_PARAM']; |
309
|
|
|
} |
310
|
|
|
} |
311
|
|
|
} |
312
|
|
|
// Set $directoryLevel so the following evaluation of the nextActive will not return |
313
|
|
|
// an invalid value if .special=directory was set |
314
|
|
|
$directoryLevel = 0; |
315
|
|
|
if (($this->conf['special'] ?? '') === 'directory') { |
316
|
|
|
$value = $this->parent_cObj->stdWrapValue('value', $this->conf['special.'] ?? [], null); |
317
|
|
|
if ($value === '') { |
318
|
|
|
$value = $tsfe->page['uid']; |
319
|
|
|
} |
320
|
|
|
$directoryLevel = (int)$tsfe->tmpl->getRootlineLevel($value); |
321
|
|
|
} |
322
|
|
|
// Setting "nextActive": This is the page uid + MPvar of the NEXT page in rootline. Used to expand the menu if we are in the right branch of the tree |
323
|
|
|
// Notice: The automatic expansion of a menu is designed to work only when no "special" modes (except "directory") are used. |
324
|
|
|
$startLevel = $directoryLevel ?: $this->entryLevel; |
325
|
|
|
$currentLevel = $startLevel + $this->menuNumber; |
326
|
|
|
if (is_array($this->tmpl->rootLine[$currentLevel] ?? null)) { |
327
|
|
|
$nextMParray = $this->MP_array; |
328
|
|
|
if (empty($nextMParray) && !($this->tmpl->rootLine[$currentLevel]['_MOUNT_OL'] ?? false) && $currentLevel > 0) { |
329
|
|
|
// Make sure to slide-down any mount point information (_MP_PARAM) to children records in the rootline |
330
|
|
|
// otherwise automatic expansion will not work |
331
|
|
|
$parentRecord = $this->tmpl->rootLine[$currentLevel - 1]; |
332
|
|
|
if (isset($parentRecord['_MP_PARAM'])) { |
333
|
|
|
$nextMParray[] = $parentRecord['_MP_PARAM']; |
334
|
|
|
} |
335
|
|
|
} |
336
|
|
|
// In overlay mode, add next level MPvars as well: |
337
|
|
|
if ($this->tmpl->rootLine[$currentLevel]['_MOUNT_OL'] ?? false) { |
338
|
|
|
$nextMParray[] = $this->tmpl->rootLine[$currentLevel]['_MP_PARAM']; |
339
|
|
|
} |
340
|
|
|
$this->nextActive = $this->tmpl->rootLine[$currentLevel]['uid'] . |
341
|
|
|
( |
342
|
|
|
!empty($nextMParray) |
343
|
|
|
? ':' . implode(',', $nextMParray) |
344
|
|
|
: '' |
345
|
|
|
); |
346
|
|
|
} else { |
347
|
|
|
$this->nextActive = ''; |
348
|
|
|
} |
349
|
|
|
return true; |
350
|
|
|
} |
351
|
|
|
$this->getTimeTracker()->setTSlogMessage('ERROR in menu', 3); |
352
|
|
|
return false; |
353
|
|
|
} |
354
|
|
|
|
355
|
|
|
/** |
356
|
|
|
* Creates the menu in the internal variables, ready for output. |
357
|
|
|
* Basically this will read the page records needed and fill in the internal $this->menuArr |
358
|
|
|
* Based on a hash of this array and some other variables the $this->result variable will be |
359
|
|
|
* loaded either from cache OR by calling the generate() method of the class to create the menu for real. |
360
|
|
|
*/ |
361
|
|
|
public function makeMenu() |
362
|
|
|
{ |
363
|
|
|
if (!$this->id) { |
364
|
|
|
return; |
365
|
|
|
} |
366
|
|
|
|
367
|
|
|
// Initializing showAccessRestrictedPages |
368
|
|
|
$SAVED_where_groupAccess = ''; |
369
|
|
|
if ($this->mconf['showAccessRestrictedPages'] ?? false) { |
370
|
|
|
// SAVING where_groupAccess |
371
|
|
|
$SAVED_where_groupAccess = $this->sys_page->where_groupAccess; |
372
|
|
|
// Temporarily removing fe_group checking! |
373
|
|
|
$this->sys_page->where_groupAccess = ''; |
374
|
|
|
} |
375
|
|
|
|
376
|
|
|
$menuItems = $this->prepareMenuItems(); |
377
|
|
|
|
378
|
|
|
$c = 0; |
379
|
|
|
$c_b = 0; |
380
|
|
|
|
381
|
|
|
$minItems = (int)(($this->mconf['minItems'] ?? 0) ?: ($this->conf['minItems'] ?? 0)); |
382
|
|
|
$maxItems = (int)(($this->mconf['maxItems'] ?? 0) ?: ($this->conf['maxItems'] ?? 0)); |
383
|
|
|
$begin = $this->parent_cObj->calc(($this->mconf['begin'] ?? 0) ?: ($this->conf['begin'] ?? 0)); |
384
|
|
|
$minItemsConf = $this->mconf['minItems.'] ?? $this->conf['minItems.'] ?? null; |
385
|
|
|
$minItems = is_array($minItemsConf) ? $this->parent_cObj->stdWrap($minItems, $minItemsConf) : $minItems; |
386
|
|
|
$maxItemsConf = $this->mconf['maxItems.'] ?? $this->conf['maxItems.'] ?? null; |
387
|
|
|
$maxItems = is_array($maxItemsConf) ? $this->parent_cObj->stdWrap($maxItems, $maxItemsConf) : $maxItems; |
388
|
|
|
$beginConf = $this->mconf['begin.'] ?? $this->conf['begin.'] ?? null; |
389
|
|
|
$begin = is_array($beginConf) ? $this->parent_cObj->stdWrap($begin, $beginConf) : $begin; |
390
|
|
|
$banUidArray = $this->getBannedUids(); |
391
|
|
|
// Fill in the menuArr with elements that should go into the menu: |
392
|
|
|
$this->menuArr = []; |
393
|
|
|
foreach ($menuItems as $data) { |
394
|
|
|
$isSpacerPage = (int)($data['doktype'] ?? 0) === PageRepository::DOKTYPE_SPACER || ($data['ITEM_STATE'] ?? '') === 'SPC'; |
395
|
|
|
// if item is a spacer, $spacer is set |
396
|
|
|
if ($this->filterMenuPages($data, $banUidArray, $isSpacerPage)) { |
397
|
|
|
$c_b++; |
398
|
|
|
// If the beginning item has been reached. |
399
|
|
|
if ($begin <= $c_b) { |
400
|
|
|
$this->menuArr[$c] = $this->determineOriginalShortcutPage($data); |
401
|
|
|
$this->menuArr[$c]['isSpacer'] = $isSpacerPage; |
402
|
|
|
$c++; |
403
|
|
|
if ($maxItems && $c >= $maxItems) { |
404
|
|
|
break; |
405
|
|
|
} |
406
|
|
|
} |
407
|
|
|
} |
408
|
|
|
} |
409
|
|
|
// Fill in fake items, if min-items is set. |
410
|
|
|
if ($minItems) { |
411
|
|
|
while ($c < $minItems) { |
412
|
|
|
$this->menuArr[$c] = [ |
413
|
|
|
'title' => '...', |
414
|
|
|
'uid' => $this->getTypoScriptFrontendController()->id |
415
|
|
|
]; |
416
|
|
|
$c++; |
417
|
|
|
} |
418
|
|
|
} |
419
|
|
|
// Passing the menuArr through a user defined function: |
420
|
|
|
if ($this->mconf['itemArrayProcFunc'] ?? false) { |
421
|
|
|
$this->menuArr = $this->userProcess('itemArrayProcFunc', $this->menuArr); |
422
|
|
|
} |
423
|
|
|
// Setting number of menu items |
424
|
|
|
$this->getTypoScriptFrontendController()->register['count_menuItems'] = count($this->menuArr); |
425
|
|
|
$this->hash = md5( |
426
|
|
|
json_encode($this->menuArr) . |
427
|
|
|
json_encode($this->mconf) . |
428
|
|
|
json_encode($this->tmpl->rootLine) . |
429
|
|
|
json_encode($this->MP_array) |
430
|
|
|
); |
431
|
|
|
// Get the cache timeout: |
432
|
|
|
if ($this->conf['cache_period'] ?? false) { |
433
|
|
|
$cacheTimeout = $this->conf['cache_period']; |
434
|
|
|
} else { |
435
|
|
|
$cacheTimeout = $this->getTypoScriptFrontendController()->get_cache_timeout(); |
436
|
|
|
} |
437
|
|
|
$cache = $this->getCache(); |
438
|
|
|
$cachedData = $cache->get($this->hash); |
439
|
|
|
if (!is_array($cachedData)) { |
440
|
|
|
$this->generate(); |
441
|
|
|
$cache->set($this->hash, $this->result, ['ident_MENUDATA'], (int)$cacheTimeout); |
442
|
|
|
} else { |
443
|
|
|
$this->result = $cachedData; |
444
|
|
|
} |
445
|
|
|
// End showAccessRestrictedPages |
446
|
|
|
if ($this->mconf['showAccessRestrictedPages'] ?? false) { |
447
|
|
|
// RESTORING where_groupAccess |
448
|
|
|
$this->sys_page->where_groupAccess = $SAVED_where_groupAccess; |
449
|
|
|
} |
450
|
|
|
} |
451
|
|
|
|
452
|
|
|
/** |
453
|
|
|
* Generates the the menu data. |
454
|
|
|
* |
455
|
|
|
* Subclasses should overwrite this method. |
456
|
|
|
*/ |
457
|
|
|
public function generate() |
458
|
|
|
{ |
459
|
|
|
} |
460
|
|
|
|
461
|
|
|
/** |
462
|
|
|
* @return string The HTML for the menu |
463
|
|
|
*/ |
464
|
|
|
public function writeMenu() |
465
|
|
|
{ |
466
|
|
|
return ''; |
467
|
|
|
} |
468
|
|
|
|
469
|
|
|
/** |
470
|
|
|
* Gets an array of page rows and removes all, which are not accessible |
471
|
|
|
* |
472
|
|
|
* @param array $pages |
473
|
|
|
* @return array |
474
|
|
|
*/ |
475
|
|
|
protected function removeInaccessiblePages(array $pages) |
476
|
|
|
{ |
477
|
|
|
$banned = $this->getBannedUids(); |
478
|
|
|
$filteredPages = []; |
479
|
|
|
foreach ($pages as $aPage) { |
480
|
|
|
if ($this->filterMenuPages($aPage, $banned, (int)$aPage['doktype'] === PageRepository::DOKTYPE_SPACER)) { |
481
|
|
|
$filteredPages[$aPage['uid']] = $aPage; |
482
|
|
|
} |
483
|
|
|
} |
484
|
|
|
return $filteredPages; |
485
|
|
|
} |
486
|
|
|
|
487
|
|
|
/** |
488
|
|
|
* Main function for retrieving menu items based on the menu type (special or sectionIndex or "normal") |
489
|
|
|
* |
490
|
|
|
* @return array |
491
|
|
|
*/ |
492
|
|
|
protected function prepareMenuItems() |
493
|
|
|
{ |
494
|
|
|
$menuItems = []; |
495
|
|
|
$alternativeSortingField = trim($this->mconf['alternativeSortingField'] ?? '') ?: 'sorting'; |
496
|
|
|
|
497
|
|
|
// Additional where clause, usually starts with AND (as usual with all additionalWhere functionality in TS) |
498
|
|
|
$additionalWhere = $this->parent_cObj->stdWrapValue('additionalWhere', $this->mconf ?? []); |
499
|
|
|
$additionalWhere .= $this->getDoktypeExcludeWhere(); |
500
|
|
|
|
501
|
|
|
// ... only for the FIRST level of a HMENU |
502
|
|
|
if ($this->menuNumber == 1 && ($this->conf['special'] ?? false)) { |
503
|
|
|
$value = (string)$this->parent_cObj->stdWrapValue('value', $this->conf['special.'] ?? [], null); |
504
|
|
|
switch ($this->conf['special']) { |
505
|
|
|
case 'userfunction': |
506
|
|
|
$menuItems = $this->prepareMenuItemsForUserSpecificMenu($value, $alternativeSortingField); |
507
|
|
|
break; |
508
|
|
|
case 'language': |
509
|
|
|
$menuItems = $this->prepareMenuItemsForLanguageMenu($value); |
510
|
|
|
break; |
511
|
|
|
case 'directory': |
512
|
|
|
$menuItems = $this->prepareMenuItemsForDirectoryMenu($value, $alternativeSortingField); |
513
|
|
|
break; |
514
|
|
|
case 'list': |
515
|
|
|
$menuItems = $this->prepareMenuItemsForListMenu($value); |
516
|
|
|
break; |
517
|
|
|
case 'updated': |
518
|
|
|
$menuItems = $this->prepareMenuItemsForUpdatedMenu( |
519
|
|
|
$value, |
520
|
|
|
$this->mconf['alternativeSortingField'] ?: false |
|
|
|
|
521
|
|
|
); |
522
|
|
|
break; |
523
|
|
|
case 'keywords': |
524
|
|
|
$menuItems = $this->prepareMenuItemsForKeywordsMenu( |
525
|
|
|
$value, |
526
|
|
|
$this->mconf['alternativeSortingField'] ?: false |
|
|
|
|
527
|
|
|
); |
528
|
|
|
break; |
529
|
|
|
case 'categories': |
530
|
|
|
/** @var CategoryMenuUtility $categoryMenuUtility */ |
531
|
|
|
$categoryMenuUtility = GeneralUtility::makeInstance(CategoryMenuUtility::class); |
532
|
|
|
$menuItems = $categoryMenuUtility->collectPages($value, $this->conf['special.'], $this); |
533
|
|
|
break; |
534
|
|
|
case 'rootline': |
535
|
|
|
$menuItems = $this->prepareMenuItemsForRootlineMenu(); |
536
|
|
|
break; |
537
|
|
|
case 'browse': |
538
|
|
|
$menuItems = $this->prepareMenuItemsForBrowseMenu($value, $alternativeSortingField, $additionalWhere); |
539
|
|
|
break; |
540
|
|
|
} |
541
|
|
|
if ($this->mconf['sectionIndex'] ?? false) { |
542
|
|
|
$sectionIndexes = []; |
543
|
|
|
foreach ($menuItems as $page) { |
544
|
|
|
$sectionIndexes = $sectionIndexes + $this->sectionIndex($alternativeSortingField, $page['uid']); |
545
|
|
|
} |
546
|
|
|
$menuItems = $sectionIndexes; |
547
|
|
|
} |
548
|
|
|
} elseif ($this->alternativeMenuTempArray !== []) { |
549
|
|
|
// Setting $menuItems array if not level 1. |
550
|
|
|
$menuItems = $this->alternativeMenuTempArray; |
551
|
|
|
} elseif ($this->mconf['sectionIndex'] ?? false) { |
552
|
|
|
$menuItems = $this->sectionIndex($alternativeSortingField); |
553
|
|
|
} else { |
554
|
|
|
// Default: Gets a hierarchical menu based on subpages of $this->id |
555
|
|
|
$menuItems = $this->sys_page->getMenu($this->id, '*', $alternativeSortingField, $additionalWhere); |
556
|
|
|
} |
557
|
|
|
return $menuItems; |
558
|
|
|
} |
559
|
|
|
|
560
|
|
|
/** |
561
|
|
|
* Fetches all menuitems if special = userfunction is set |
562
|
|
|
* |
563
|
|
|
* @param string $specialValue The value from special.value |
564
|
|
|
* @param string $sortingField The sorting field |
565
|
|
|
* @return array |
566
|
|
|
*/ |
567
|
|
|
protected function prepareMenuItemsForUserSpecificMenu($specialValue, $sortingField) |
568
|
|
|
{ |
569
|
|
|
$menuItems = $this->parent_cObj->callUserFunction( |
570
|
|
|
$this->conf['special.']['userFunc'], |
571
|
|
|
array_merge($this->conf['special.'], ['value' => $specialValue, '_altSortField' => $sortingField]), |
572
|
|
|
'' |
573
|
|
|
); |
574
|
|
|
return is_array($menuItems) ? $menuItems : []; |
575
|
|
|
} |
576
|
|
|
|
577
|
|
|
/** |
578
|
|
|
* Fetches all menuitems if special = language is set |
579
|
|
|
* |
580
|
|
|
* @param string $specialValue The value from special.value |
581
|
|
|
* @return array |
582
|
|
|
*/ |
583
|
|
|
protected function prepareMenuItemsForLanguageMenu($specialValue) |
584
|
|
|
{ |
585
|
|
|
$menuItems = []; |
586
|
|
|
// Getting current page record NOT overlaid by any translation: |
587
|
|
|
$tsfe = $this->getTypoScriptFrontendController(); |
588
|
|
|
$currentPageWithNoOverlay = $this->sys_page->getRawRecord('pages', $tsfe->page['uid']); |
589
|
|
|
|
590
|
|
|
if ($specialValue === 'auto') { |
591
|
|
|
$site = $this->getCurrentSite(); |
592
|
|
|
$languages = $site->getLanguages(); |
593
|
|
|
$languageItems = array_keys($languages); |
594
|
|
|
} else { |
595
|
|
|
$languageItems = GeneralUtility::intExplode(',', $specialValue); |
596
|
|
|
} |
597
|
|
|
|
598
|
|
|
$tsfe->register['languages_HMENU'] = implode(',', $languageItems); |
599
|
|
|
|
600
|
|
|
$currentLanguageId = $this->getCurrentLanguageAspect()->getId(); |
601
|
|
|
|
602
|
|
|
foreach ($languageItems as $sUid) { |
603
|
|
|
// Find overlay record: |
604
|
|
|
if ($sUid) { |
605
|
|
|
$lRecs = $this->sys_page->getPageOverlay($tsfe->page['uid'], $sUid); |
606
|
|
|
} else { |
607
|
|
|
$lRecs = []; |
608
|
|
|
} |
609
|
|
|
// Checking if the "disabled" state should be set. |
610
|
|
|
$pageTranslationVisibility = new PageTranslationVisibility((int)($tsfe->page['l18n_cfg'] ?? 0)); |
611
|
|
|
if ($pageTranslationVisibility->shouldHideTranslationIfNoTranslatedRecordExists() && $sUid && |
612
|
|
|
empty($lRecs) || $pageTranslationVisibility->shouldBeHiddenInDefaultLanguage() && |
613
|
|
|
(!$sUid || empty($lRecs)) || |
614
|
|
|
!($this->conf['special.']['normalWhenNoLanguage'] ?? false) && $sUid && empty($lRecs) |
615
|
|
|
) { |
616
|
|
|
$iState = $currentLanguageId === $sUid ? 'USERDEF2' : 'USERDEF1'; |
617
|
|
|
} else { |
618
|
|
|
$iState = $currentLanguageId === $sUid ? 'ACT' : 'NO'; |
619
|
|
|
} |
620
|
|
|
$getVars = ''; |
621
|
|
|
if ($this->conf['addQueryString']) { |
622
|
|
|
$getVars = $this->parent_cObj->getQueryArguments($this->conf['addQueryString.']); |
623
|
|
|
} |
624
|
|
|
// Adding menu item: |
625
|
|
|
$menuItems[] = array_merge( |
626
|
|
|
array_merge($currentPageWithNoOverlay, $lRecs), |
|
|
|
|
627
|
|
|
[ |
628
|
|
|
'_PAGES_OVERLAY_REQUESTEDLANGUAGE' => $sUid, |
629
|
|
|
'ITEM_STATE' => $iState, |
630
|
|
|
'_ADD_GETVARS' => $getVars, |
631
|
|
|
'_SAFE' => true |
632
|
|
|
] |
633
|
|
|
); |
634
|
|
|
} |
635
|
|
|
return $menuItems; |
636
|
|
|
} |
637
|
|
|
|
638
|
|
|
/** |
639
|
|
|
* Fetches all menuitems if special = directory is set |
640
|
|
|
* |
641
|
|
|
* @param string $specialValue The value from special.value |
642
|
|
|
* @param string $sortingField The sorting field |
643
|
|
|
* @return array |
644
|
|
|
*/ |
645
|
|
|
protected function prepareMenuItemsForDirectoryMenu($specialValue, $sortingField) |
646
|
|
|
{ |
647
|
|
|
$tsfe = $this->getTypoScriptFrontendController(); |
648
|
|
|
$menuItems = []; |
649
|
|
|
if ($specialValue == '') { |
650
|
|
|
$specialValue = $tsfe->page['uid']; |
651
|
|
|
} |
652
|
|
|
$items = GeneralUtility::intExplode(',', $specialValue); |
653
|
|
|
$pageLinkBuilder = GeneralUtility::makeInstance(PageLinkBuilder::class, $this->parent_cObj); |
654
|
|
|
foreach ($items as $id) { |
655
|
|
|
$MP = $pageLinkBuilder->getMountPointParameterFromRootPointMaps($id); |
656
|
|
|
// Checking if a page is a mount page and if so, change the ID and set the MP var properly. |
657
|
|
|
$mount_info = $this->sys_page->getMountPointInfo($id); |
658
|
|
|
if (is_array($mount_info)) { |
659
|
|
|
if ($mount_info['overlay']) { |
660
|
|
|
// Overlays should already have their full MPvars calculated: |
661
|
|
|
$MP = $pageLinkBuilder->getMountPointParameterFromRootPointMaps((int)$mount_info['mount_pid']); |
662
|
|
|
$MP = $MP ?: $mount_info['MPvar']; |
663
|
|
|
} else { |
664
|
|
|
$MP = ($MP ? $MP . ',' : '') . $mount_info['MPvar']; |
665
|
|
|
} |
666
|
|
|
$id = $mount_info['mount_pid']; |
667
|
|
|
} |
668
|
|
|
// Get sub-pages: |
669
|
|
|
$statement = $this->parent_cObj->exec_getQuery('pages', ['pidInList' => $id, 'orderBy' => $sortingField]); |
670
|
|
|
while ($row = $statement->fetch()) { |
671
|
|
|
// When the site language configuration is in "free" mode, then the page without overlay is fetched |
672
|
|
|
// (which is kind-of strange for pages, but this is what exec_getQuery() is doing) |
673
|
|
|
// this means, that $row is a translated page, but hasn't been overlaid. For this reason, we fetch |
674
|
|
|
// the default translation page again, (which does a ->getPageOverlay() again - doing this on a |
675
|
|
|
// translated page would result in no record at all) |
676
|
|
|
if ($row['l10n_parent'] > 0 && !isset($row['_PAGES_OVERLAY'])) { |
677
|
|
|
$row = $this->sys_page->getPage($row['l10n_parent'], true); |
678
|
|
|
} |
679
|
|
|
$tsfe->sys_page->versionOL('pages', $row, true); |
680
|
|
|
if (!empty($row)) { |
681
|
|
|
// Keep mount point? |
682
|
|
|
$mount_info = $this->sys_page->getMountPointInfo($row['uid'], $row); |
683
|
|
|
// There is a valid mount point. |
684
|
|
|
if (is_array($mount_info) && $mount_info['overlay']) { |
685
|
|
|
// Using "getPage" is OK since we need the check for enableFields |
686
|
|
|
// AND for type 2 of mount pids we DO require a doktype < 200! |
687
|
|
|
$mp_row = $this->sys_page->getPage($mount_info['mount_pid']); |
688
|
|
|
if (!empty($mp_row)) { |
689
|
|
|
$row = $mp_row; |
690
|
|
|
$row['_MP_PARAM'] = $mount_info['MPvar']; |
691
|
|
|
} else { |
692
|
|
|
// If the mount point could not be fetched with respect |
693
|
|
|
// to enableFields, unset the row so it does not become a part of the menu! |
694
|
|
|
unset($row); |
695
|
|
|
} |
696
|
|
|
} |
697
|
|
|
// Add external MP params, then the row: |
698
|
|
|
if (!empty($row)) { |
699
|
|
|
if ($MP) { |
700
|
|
|
$row['_MP_PARAM'] = $MP . ($row['_MP_PARAM'] ? ',' . $row['_MP_PARAM'] : ''); |
701
|
|
|
} |
702
|
|
|
$menuItems[] = $this->sys_page->getPageOverlay($row); |
703
|
|
|
} |
704
|
|
|
} |
705
|
|
|
} |
706
|
|
|
} |
707
|
|
|
|
708
|
|
|
return $menuItems; |
709
|
|
|
} |
710
|
|
|
|
711
|
|
|
/** |
712
|
|
|
* Fetches all menuitems if special = list is set |
713
|
|
|
* |
714
|
|
|
* @param string $specialValue The value from special.value |
715
|
|
|
* @return array |
716
|
|
|
*/ |
717
|
|
|
protected function prepareMenuItemsForListMenu($specialValue) |
718
|
|
|
{ |
719
|
|
|
$menuItems = []; |
720
|
|
|
if ($specialValue == '') { |
721
|
|
|
$specialValue = $this->id; |
722
|
|
|
} |
723
|
|
|
$skippedEnableFields = []; |
724
|
|
|
if (!empty($this->mconf['showAccessRestrictedPages'])) { |
725
|
|
|
$skippedEnableFields = ['fe_group' => 1]; |
726
|
|
|
} |
727
|
|
|
/** @var RelationHandler $loadDB*/ |
728
|
|
|
$loadDB = GeneralUtility::makeInstance(RelationHandler::class); |
729
|
|
|
$loadDB->setFetchAllFields(true); |
730
|
|
|
$loadDB->start($specialValue, 'pages'); |
731
|
|
|
$loadDB->additionalWhere['pages'] = $this->sys_page->enableFields('pages', -1, $skippedEnableFields); |
732
|
|
|
$loadDB->getFromDB(); |
733
|
|
|
$pageLinkBuilder = GeneralUtility::makeInstance(PageLinkBuilder::class, $this->parent_cObj); |
734
|
|
|
foreach ($loadDB->itemArray as $val) { |
735
|
|
|
$MP = $pageLinkBuilder->getMountPointParameterFromRootPointMaps((int)$val['id']); |
736
|
|
|
// Keep mount point? |
737
|
|
|
$mount_info = $this->sys_page->getMountPointInfo($val['id']); |
738
|
|
|
// There is a valid mount point. |
739
|
|
|
if (is_array($mount_info) && $mount_info['overlay']) { |
740
|
|
|
// Using "getPage" is OK since we need the check for enableFields |
741
|
|
|
// AND for type 2 of mount pids we DO require a doktype < 200! |
742
|
|
|
$mp_row = $this->sys_page->getPage($mount_info['mount_pid']); |
743
|
|
|
if (!empty($mp_row)) { |
744
|
|
|
$row = $mp_row; |
745
|
|
|
$row['_MP_PARAM'] = $mount_info['MPvar']; |
746
|
|
|
// Overlays should already have their full MPvars calculated |
747
|
|
|
if ($mount_info['overlay']) { |
748
|
|
|
$MP = $pageLinkBuilder->getMountPointParameterFromRootPointMaps((int)$mount_info['mount_pid']); |
749
|
|
|
if ($MP) { |
750
|
|
|
unset($row['_MP_PARAM']); |
751
|
|
|
} |
752
|
|
|
} |
753
|
|
|
} else { |
754
|
|
|
// If the mount point could not be fetched with respect to |
755
|
|
|
// enableFields, unset the row so it does not become a part of the menu! |
756
|
|
|
unset($row); |
757
|
|
|
} |
758
|
|
|
} else { |
759
|
|
|
$row = $loadDB->results['pages'][$val['id']] ?? []; |
760
|
|
|
} |
761
|
|
|
// Add versioning overlay for current page (to respect workspaces) |
762
|
|
|
if (isset($row) && is_array($row)) { |
763
|
|
|
$this->sys_page->versionOL('pages', $row, true); |
764
|
|
|
} |
765
|
|
|
// Add external MP params, then the row: |
766
|
|
|
if (isset($row) && is_array($row)) { |
767
|
|
|
if ($MP) { |
768
|
|
|
$row['_MP_PARAM'] = $MP . ($row['_MP_PARAM'] ? ',' . $row['_MP_PARAM'] : ''); |
769
|
|
|
} |
770
|
|
|
$menuItems[] = $this->sys_page->getPageOverlay($row); |
771
|
|
|
} |
772
|
|
|
} |
773
|
|
|
return $menuItems; |
774
|
|
|
} |
775
|
|
|
|
776
|
|
|
/** |
777
|
|
|
* Fetches all menuitems if special = updated is set |
778
|
|
|
* |
779
|
|
|
* @param string $specialValue The value from special.value |
780
|
|
|
* @param string $sortingField The sorting field |
781
|
|
|
* @return array |
782
|
|
|
*/ |
783
|
|
|
protected function prepareMenuItemsForUpdatedMenu($specialValue, $sortingField) |
784
|
|
|
{ |
785
|
|
|
$tsfe = $this->getTypoScriptFrontendController(); |
786
|
|
|
$menuItems = []; |
787
|
|
|
if ($specialValue == '') { |
788
|
|
|
$specialValue = $tsfe->page['uid']; |
789
|
|
|
} |
790
|
|
|
$items = GeneralUtility::intExplode(',', $specialValue); |
791
|
|
|
if (MathUtility::canBeInterpretedAsInteger($this->conf['special.']['depth'] ?? null)) { |
792
|
|
|
$depth = MathUtility::forceIntegerInRange($this->conf['special.']['depth'], 1, 20); |
793
|
|
|
} else { |
794
|
|
|
$depth = 20; |
795
|
|
|
} |
796
|
|
|
// Max number of items |
797
|
|
|
$limit = MathUtility::forceIntegerInRange(($this->conf['special.']['limit'] ?? 0), 0, 100); |
798
|
|
|
$maxAge = (int)$this->parent_cObj->calc($this->conf['special.']['maxAge']); |
799
|
|
|
if (!$limit) { |
800
|
|
|
$limit = 10; |
801
|
|
|
} |
802
|
|
|
// 'auto', 'manual', 'tstamp' |
803
|
|
|
$mode = $this->conf['special.']['mode'] ?? ''; |
804
|
|
|
// Get id's |
805
|
|
|
$beginAtLevel = MathUtility::forceIntegerInRange(($this->conf['special.']['beginAtLevel'] ?? 0), 0, 100); |
806
|
|
|
$id_list_arr = []; |
807
|
|
|
foreach ($items as $id) { |
808
|
|
|
// Exclude the current ID if beginAtLevel is > 0 |
809
|
|
|
if ($beginAtLevel > 0) { |
810
|
|
|
$id_list_arr[] = $this->parent_cObj->getTreeList($id, $depth - 1 + $beginAtLevel, $beginAtLevel - 1); |
811
|
|
|
} else { |
812
|
|
|
$id_list_arr[] = $this->parent_cObj->getTreeList(-1 * $id, $depth - 1 + $beginAtLevel, $beginAtLevel - 1); |
813
|
|
|
} |
814
|
|
|
} |
815
|
|
|
$id_list = implode(',', $id_list_arr); |
816
|
|
|
// Get sortField (mode) |
817
|
|
|
$sortField = $this->getMode($mode); |
818
|
|
|
|
819
|
|
|
$extraWhere = ($this->conf['includeNotInMenu'] ? '' : ' AND pages.nav_hide=0') . $this->getDoktypeExcludeWhere(); |
820
|
|
|
if ($this->conf['special.']['excludeNoSearchPages']) { |
821
|
|
|
$extraWhere .= ' AND pages.no_search=0'; |
822
|
|
|
} |
823
|
|
|
if ($maxAge > 0) { |
824
|
|
|
$extraWhere .= ' AND ' . $sortField . '>' . ($GLOBALS['SIM_ACCESS_TIME'] - $maxAge); |
825
|
|
|
} |
826
|
|
|
$statement = $this->parent_cObj->exec_getQuery('pages', [ |
827
|
|
|
'pidInList' => '0', |
828
|
|
|
'uidInList' => $id_list, |
829
|
|
|
'where' => $sortField . '>=0' . $extraWhere, |
830
|
|
|
'orderBy' => $sortingField ?: $sortField . ' DESC', |
831
|
|
|
'max' => $limit |
832
|
|
|
]); |
833
|
|
|
while ($row = $statement->fetch()) { |
834
|
|
|
// When the site language configuration is in "free" mode, then the page without overlay is fetched |
835
|
|
|
// (which is kind-of strange for pages, but this is what exec_getQuery() is doing) |
836
|
|
|
// this means, that $row is a translated page, but hasn't been overlaid. For this reason, we fetch |
837
|
|
|
// the default translation page again, (which does a ->getPageOverlay() again - doing this on a |
838
|
|
|
// translated page would result in no record at all) |
839
|
|
|
if ($row['l10n_parent'] > 0 && !isset($row['_PAGES_OVERLAY'])) { |
840
|
|
|
$row = $this->sys_page->getPage($row['l10n_parent'], true); |
841
|
|
|
} |
842
|
|
|
$tsfe->sys_page->versionOL('pages', $row, true); |
843
|
|
|
if (is_array($row)) { |
844
|
|
|
$menuItems[$row['uid']] = $this->sys_page->getPageOverlay($row); |
845
|
|
|
} |
846
|
|
|
} |
847
|
|
|
|
848
|
|
|
return $menuItems; |
849
|
|
|
} |
850
|
|
|
|
851
|
|
|
/** |
852
|
|
|
* Fetches all menuitems if special = keywords is set |
853
|
|
|
* |
854
|
|
|
* @param string $specialValue The value from special.value |
855
|
|
|
* @param string $sortingField The sorting field |
856
|
|
|
* @return array |
857
|
|
|
*/ |
858
|
|
|
protected function prepareMenuItemsForKeywordsMenu($specialValue, $sortingField) |
859
|
|
|
{ |
860
|
|
|
$tsfe = $this->getTypoScriptFrontendController(); |
861
|
|
|
$menuItems = []; |
862
|
|
|
[$specialValue] = GeneralUtility::intExplode(',', $specialValue); |
863
|
|
|
if (!$specialValue) { |
864
|
|
|
$specialValue = $tsfe->page['uid']; |
865
|
|
|
} |
866
|
|
|
if (($this->conf['special.']['setKeywords'] ?? false) || ($this->conf['special.']['setKeywords.'] ?? false)) { |
867
|
|
|
$kw = (string)$this->parent_cObj->stdWrapValue('setKeywords', $this->conf['special.'] ?? []); |
868
|
|
|
} else { |
869
|
|
|
// The page record of the 'value'. |
870
|
|
|
$value_rec = $this->sys_page->getPage($specialValue); |
871
|
|
|
$kfieldSrc = ($this->conf['special.']['keywordsField.']['sourceField'] ?? false) ? $this->conf['special.']['keywordsField.']['sourceField'] : 'keywords'; |
872
|
|
|
// keywords. |
873
|
|
|
$kw = trim($this->parent_cObj->keywords($value_rec[$kfieldSrc])); |
874
|
|
|
} |
875
|
|
|
// *'auto', 'manual', 'tstamp' |
876
|
|
|
$mode = $this->conf['special.']['mode'] ?? ''; |
877
|
|
|
$sortField = $this->getMode($mode); |
878
|
|
|
// Depth, limit, extra where |
879
|
|
|
if (MathUtility::canBeInterpretedAsInteger($this->conf['special.']['depth'] ?? null)) { |
880
|
|
|
$depth = MathUtility::forceIntegerInRange($this->conf['special.']['depth'], 0, 20); |
881
|
|
|
} else { |
882
|
|
|
$depth = 20; |
883
|
|
|
} |
884
|
|
|
// Max number of items |
885
|
|
|
$limit = MathUtility::forceIntegerInRange(($this->conf['special.']['limit'] ?? 0), 0, 100); |
886
|
|
|
// Start point |
887
|
|
|
$eLevel = $this->parent_cObj->getKey( |
888
|
|
|
$this->parent_cObj->stdWrapValue('entryLevel', $this->conf['special.'] ?? []), |
|
|
|
|
889
|
|
|
$this->tmpl->rootLine |
890
|
|
|
); |
891
|
|
|
$startUid = (int)$this->tmpl->rootLine[$eLevel]['uid']; |
892
|
|
|
// Which field is for keywords |
893
|
|
|
$kfield = 'keywords'; |
894
|
|
|
if ($this->conf['special.']['keywordsField'] ?? false) { |
895
|
|
|
[$kfield] = explode(' ', trim($this->conf['special.']['keywordsField'])); |
896
|
|
|
} |
897
|
|
|
// If there are keywords and the startuid is present |
898
|
|
|
if ($kw && $startUid) { |
899
|
|
|
$bA = MathUtility::forceIntegerInRange(($this->conf['special.']['beginAtLevel'] ?? 0), 0, 100); |
900
|
|
|
$id_list = $this->parent_cObj->getTreeList(-1 * $startUid, $depth - 1 + $bA, $bA - 1); |
901
|
|
|
$kwArr = GeneralUtility::trimExplode(',', $kw, true); |
902
|
|
|
$keyWordsWhereArr = []; |
903
|
|
|
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages'); |
904
|
|
|
foreach ($kwArr as $word) { |
905
|
|
|
$keyWordsWhereArr[] = $queryBuilder->expr()->like( |
906
|
|
|
$kfield, |
907
|
|
|
$queryBuilder->createNamedParameter( |
908
|
|
|
'%' . $queryBuilder->escapeLikeWildcards($word) . '%', |
909
|
|
|
\PDO::PARAM_STR |
910
|
|
|
) |
911
|
|
|
); |
912
|
|
|
} |
913
|
|
|
$queryBuilder |
914
|
|
|
->select('*') |
915
|
|
|
->from('pages') |
916
|
|
|
->where( |
917
|
|
|
$queryBuilder->expr()->in( |
918
|
|
|
'uid', |
919
|
|
|
GeneralUtility::intExplode(',', $id_list, true) |
920
|
|
|
), |
921
|
|
|
$queryBuilder->expr()->neq( |
922
|
|
|
'uid', |
923
|
|
|
$queryBuilder->createNamedParameter($specialValue, \PDO::PARAM_INT) |
924
|
|
|
) |
925
|
|
|
); |
926
|
|
|
|
927
|
|
|
if (!empty($keyWordsWhereArr)) { |
928
|
|
|
$queryBuilder->andWhere($queryBuilder->expr()->orX(...$keyWordsWhereArr)); |
929
|
|
|
} |
930
|
|
|
|
931
|
|
|
if (!empty($this->excludedDoktypes)) { |
932
|
|
|
$queryBuilder->andWhere( |
933
|
|
|
$queryBuilder->expr()->notIn( |
934
|
|
|
'pages.doktype', |
935
|
|
|
$this->excludedDoktypes |
936
|
|
|
) |
937
|
|
|
); |
938
|
|
|
} |
939
|
|
|
|
940
|
|
|
if (!$this->conf['includeNotInMenu']) { |
941
|
|
|
$queryBuilder->andWhere($queryBuilder->expr()->eq('pages.nav_hide', 0)); |
942
|
|
|
} |
943
|
|
|
|
944
|
|
|
if ($this->conf['special.']['excludeNoSearchPages']) { |
945
|
|
|
$queryBuilder->andWhere($queryBuilder->expr()->eq('pages.no_search', 0)); |
946
|
|
|
} |
947
|
|
|
|
948
|
|
|
if ($limit > 0) { |
949
|
|
|
$queryBuilder->setMaxResults($limit); |
950
|
|
|
} |
951
|
|
|
|
952
|
|
|
if ($sortingField) { |
953
|
|
|
$queryBuilder->orderBy($sortingField); |
954
|
|
|
} else { |
955
|
|
|
$queryBuilder->orderBy($sortField, 'desc'); |
956
|
|
|
} |
957
|
|
|
|
958
|
|
|
$result = $queryBuilder->execute(); |
959
|
|
|
while ($row = $result->fetch()) { |
960
|
|
|
$tsfe->sys_page->versionOL('pages', $row, true); |
961
|
|
|
if (is_array($row)) { |
962
|
|
|
$menuItems[$row['uid']] = $this->sys_page->getPageOverlay($row); |
963
|
|
|
} |
964
|
|
|
} |
965
|
|
|
} |
966
|
|
|
|
967
|
|
|
return $menuItems; |
968
|
|
|
} |
969
|
|
|
|
970
|
|
|
/** |
971
|
|
|
* Fetches all menuitems if special = rootline is set |
972
|
|
|
* |
973
|
|
|
* @return array |
974
|
|
|
*/ |
975
|
|
|
protected function prepareMenuItemsForRootlineMenu() |
976
|
|
|
{ |
977
|
|
|
$menuItems = []; |
978
|
|
|
$range = (string)$this->parent_cObj->stdWrapValue('range', $this->conf['special.'] ?? []); |
979
|
|
|
$begin_end = explode('|', $range); |
980
|
|
|
$begin_end[0] = (int)$begin_end[0]; |
981
|
|
|
if (!MathUtility::canBeInterpretedAsInteger($begin_end[1])) { |
982
|
|
|
$begin_end[1] = -1; |
983
|
|
|
} |
984
|
|
|
$beginKey = $this->parent_cObj->getKey($begin_end[0], $this->tmpl->rootLine); |
985
|
|
|
$endKey = $this->parent_cObj->getKey($begin_end[1], $this->tmpl->rootLine); |
986
|
|
|
if ($endKey < $beginKey) { |
987
|
|
|
$endKey = $beginKey; |
988
|
|
|
} |
989
|
|
|
$rl_MParray = []; |
990
|
|
|
foreach ($this->tmpl->rootLine as $k_rl => $v_rl) { |
991
|
|
|
// For overlaid mount points, set the variable right now: |
992
|
|
|
if ($v_rl['_MP_PARAM'] && $v_rl['_MOUNT_OL']) { |
993
|
|
|
$rl_MParray[] = $v_rl['_MP_PARAM']; |
994
|
|
|
} |
995
|
|
|
// Traverse rootline: |
996
|
|
|
if ($k_rl >= $beginKey && $k_rl <= $endKey) { |
997
|
|
|
$temp_key = $k_rl; |
998
|
|
|
$menuItems[$temp_key] = $this->sys_page->getPage($v_rl['uid']); |
999
|
|
|
if (!empty($menuItems[$temp_key])) { |
1000
|
|
|
// If there are no specific target for the page, put the level specific target on. |
1001
|
|
|
if (!$menuItems[$temp_key]['target']) { |
1002
|
|
|
$menuItems[$temp_key]['target'] = $this->conf['special.']['targets.'][$k_rl]; |
1003
|
|
|
$menuItems[$temp_key]['_MP_PARAM'] = implode(',', $rl_MParray); |
1004
|
|
|
} |
1005
|
|
|
} else { |
1006
|
|
|
unset($menuItems[$temp_key]); |
1007
|
|
|
} |
1008
|
|
|
} |
1009
|
|
|
// For normal mount points, set the variable for next level. |
1010
|
|
|
if ($v_rl['_MP_PARAM'] && !$v_rl['_MOUNT_OL']) { |
1011
|
|
|
$rl_MParray[] = $v_rl['_MP_PARAM']; |
1012
|
|
|
} |
1013
|
|
|
} |
1014
|
|
|
// Reverse order of elements (e.g. "1,2,3,4" gets "4,3,2,1"): |
1015
|
|
|
if (isset($this->conf['special.']['reverseOrder']) && $this->conf['special.']['reverseOrder']) { |
1016
|
|
|
$menuItems = array_reverse($menuItems); |
1017
|
|
|
} |
1018
|
|
|
return $menuItems; |
1019
|
|
|
} |
1020
|
|
|
|
1021
|
|
|
/** |
1022
|
|
|
* Fetches all menuitems if special = browse is set |
1023
|
|
|
* |
1024
|
|
|
* @param string $specialValue The value from special.value |
1025
|
|
|
* @param string $sortingField The sorting field |
1026
|
|
|
* @param string $additionalWhere Additional WHERE clause |
1027
|
|
|
* @return array |
1028
|
|
|
*/ |
1029
|
|
|
protected function prepareMenuItemsForBrowseMenu($specialValue, $sortingField, $additionalWhere) |
1030
|
|
|
{ |
1031
|
|
|
$menuItems = []; |
1032
|
|
|
[$specialValue] = GeneralUtility::intExplode(',', $specialValue); |
1033
|
|
|
if (!$specialValue) { |
1034
|
|
|
$specialValue = $this->getTypoScriptFrontendController()->page['uid']; |
1035
|
|
|
} |
1036
|
|
|
// Will not work out of rootline |
1037
|
|
|
if ($specialValue != $this->tmpl->rootLine[0]['uid']) { |
1038
|
|
|
$recArr = []; |
1039
|
|
|
// The page record of the 'value'. |
1040
|
|
|
$value_rec = $this->sys_page->getPage($specialValue); |
1041
|
|
|
// 'up' page cannot be outside rootline |
1042
|
|
|
if ($value_rec['pid']) { |
1043
|
|
|
// The page record of 'up'. |
1044
|
|
|
$recArr['up'] = $this->sys_page->getPage($value_rec['pid']); |
1045
|
|
|
} |
1046
|
|
|
// If the 'up' item was NOT level 0 in rootline... |
1047
|
|
|
if ($recArr['up']['pid'] && $value_rec['pid'] != $this->tmpl->rootLine[0]['uid']) { |
1048
|
|
|
// The page record of "index". |
1049
|
|
|
$recArr['index'] = $this->sys_page->getPage($recArr['up']['pid']); |
1050
|
|
|
} |
1051
|
|
|
// check if certain pages should be excluded |
1052
|
|
|
$additionalWhere .= ($this->conf['includeNotInMenu'] ? '' : ' AND pages.nav_hide=0') . $this->getDoktypeExcludeWhere(); |
1053
|
|
|
if ($this->conf['special.']['excludeNoSearchPages']) { |
1054
|
|
|
$additionalWhere .= ' AND pages.no_search=0'; |
1055
|
|
|
} |
1056
|
|
|
// prev / next is found |
1057
|
|
|
$prevnext_menu = $this->removeInaccessiblePages($this->sys_page->getMenu($value_rec['pid'], '*', $sortingField, $additionalWhere)); |
1058
|
|
|
$lastKey = 0; |
1059
|
|
|
$nextActive = 0; |
1060
|
|
|
foreach ($prevnext_menu as $k_b => $v_b) { |
1061
|
|
|
if ($nextActive) { |
1062
|
|
|
$recArr['next'] = $v_b; |
1063
|
|
|
$nextActive = 0; |
1064
|
|
|
} |
1065
|
|
|
if ($v_b['uid'] == $specialValue) { |
1066
|
|
|
if ($lastKey) { |
1067
|
|
|
$recArr['prev'] = $prevnext_menu[$lastKey]; |
1068
|
|
|
} |
1069
|
|
|
$nextActive = 1; |
1070
|
|
|
} |
1071
|
|
|
$lastKey = $k_b; |
1072
|
|
|
} |
1073
|
|
|
|
1074
|
|
|
$recArr['first'] = reset($prevnext_menu); |
1075
|
|
|
$recArr['last'] = end($prevnext_menu); |
1076
|
|
|
// prevsection / nextsection is found |
1077
|
|
|
// You can only do this, if there is a valid page two levels up! |
1078
|
|
|
if (!empty($recArr['index']['uid'])) { |
1079
|
|
|
$prevnextsection_menu = $this->removeInaccessiblePages($this->sys_page->getMenu($recArr['index']['uid'], '*', $sortingField, $additionalWhere)); |
1080
|
|
|
$lastKey = 0; |
1081
|
|
|
$nextActive = 0; |
1082
|
|
|
foreach ($prevnextsection_menu as $k_b => $v_b) { |
1083
|
|
|
if ($nextActive) { |
1084
|
|
|
$sectionRec_temp = $this->removeInaccessiblePages($this->sys_page->getMenu($v_b['uid'], '*', $sortingField, $additionalWhere)); |
1085
|
|
|
if (!empty($sectionRec_temp)) { |
1086
|
|
|
$recArr['nextsection'] = reset($sectionRec_temp); |
1087
|
|
|
$recArr['nextsection_last'] = end($sectionRec_temp); |
1088
|
|
|
$nextActive = 0; |
1089
|
|
|
} |
1090
|
|
|
} |
1091
|
|
|
if ($v_b['uid'] == $value_rec['pid']) { |
1092
|
|
|
if ($lastKey) { |
1093
|
|
|
$sectionRec_temp = $this->removeInaccessiblePages($this->sys_page->getMenu($prevnextsection_menu[$lastKey]['uid'], '*', $sortingField, $additionalWhere)); |
1094
|
|
|
if (!empty($sectionRec_temp)) { |
1095
|
|
|
$recArr['prevsection'] = reset($sectionRec_temp); |
1096
|
|
|
$recArr['prevsection_last'] = end($sectionRec_temp); |
1097
|
|
|
} |
1098
|
|
|
} |
1099
|
|
|
$nextActive = 1; |
1100
|
|
|
} |
1101
|
|
|
$lastKey = $k_b; |
1102
|
|
|
} |
1103
|
|
|
} |
1104
|
|
|
if ($this->conf['special.']['items.']['prevnextToSection']) { |
1105
|
|
|
if (!is_array($recArr['prev']) && is_array($recArr['prevsection_last'])) { |
1106
|
|
|
$recArr['prev'] = $recArr['prevsection_last']; |
1107
|
|
|
} |
1108
|
|
|
if (!is_array($recArr['next']) && is_array($recArr['nextsection'])) { |
1109
|
|
|
$recArr['next'] = $recArr['nextsection']; |
1110
|
|
|
} |
1111
|
|
|
} |
1112
|
|
|
$items = explode('|', $this->conf['special.']['items']); |
1113
|
|
|
$c = 0; |
1114
|
|
|
foreach ($items as $k_b => $v_b) { |
1115
|
|
|
$v_b = strtolower(trim($v_b)); |
1116
|
|
|
if ((int)$this->conf['special.'][$v_b . '.']['uid']) { |
1117
|
|
|
$recArr[$v_b] = $this->sys_page->getPage((int)$this->conf['special.'][$v_b . '.']['uid']); |
1118
|
|
|
} |
1119
|
|
|
if (is_array($recArr[$v_b])) { |
1120
|
|
|
$menuItems[$c] = $recArr[$v_b]; |
1121
|
|
|
if ($this->conf['special.'][$v_b . '.']['target']) { |
1122
|
|
|
$menuItems[$c]['target'] = $this->conf['special.'][$v_b . '.']['target']; |
1123
|
|
|
} |
1124
|
|
|
$tmpSpecialFields = $this->conf['special.'][$v_b . '.']['fields.']; |
1125
|
|
|
if (is_array($tmpSpecialFields)) { |
1126
|
|
|
foreach ($tmpSpecialFields as $fk => $val) { |
1127
|
|
|
$menuItems[$c][$fk] = $val; |
1128
|
|
|
} |
1129
|
|
|
} |
1130
|
|
|
$c++; |
1131
|
|
|
} |
1132
|
|
|
} |
1133
|
|
|
} |
1134
|
|
|
return $menuItems; |
1135
|
|
|
} |
1136
|
|
|
|
1137
|
|
|
/** |
1138
|
|
|
* Checks if a page is OK to include in the final menu item array. Pages can be excluded if the doktype is wrong, |
1139
|
|
|
* if they are hidden in navigation, have a uid in the list of banned uids etc. |
1140
|
|
|
* |
1141
|
|
|
* @param array $data Array of menu items |
1142
|
|
|
* @param array $banUidArray Array of page uids which are to be excluded |
1143
|
|
|
* @param bool $isSpacerPage If set, then the page is a spacer. |
1144
|
|
|
* @return bool Returns TRUE if the page can be safely included. |
1145
|
|
|
* |
1146
|
|
|
* @throws \UnexpectedValueException |
1147
|
|
|
*/ |
1148
|
|
|
public function filterMenuPages(&$data, $banUidArray, $isSpacerPage) |
1149
|
|
|
{ |
1150
|
|
|
$includePage = true; |
1151
|
|
|
foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['cms/tslib/class.tslib_menu.php']['filterMenuPages'] ?? [] as $className) { |
1152
|
|
|
$hookObject = GeneralUtility::makeInstance($className); |
1153
|
|
|
if (!$hookObject instanceof AbstractMenuFilterPagesHookInterface) { |
1154
|
|
|
throw new \UnexpectedValueException($className . ' must implement interface ' . AbstractMenuFilterPagesHookInterface::class, 1269877402); |
1155
|
|
|
} |
1156
|
|
|
$includePage = $includePage && $hookObject->processFilter($data, $banUidArray, $isSpacerPage, $this); |
1157
|
|
|
} |
1158
|
|
|
if (!$includePage) { |
1159
|
|
|
return false; |
1160
|
|
|
} |
1161
|
|
|
if ($data['_SAFE'] ?? false) { |
1162
|
|
|
return true; |
1163
|
|
|
} |
1164
|
|
|
// If the spacer-function is not enabled, spacers will not enter the $menuArr |
1165
|
|
|
if (!($this->mconf['SPC'] ?? false) && $isSpacerPage) { |
1166
|
|
|
return false; |
1167
|
|
|
} |
1168
|
|
|
// Page may not be a 'Backend User Section' or any other excluded doktype |
1169
|
|
|
if (in_array((int)($data['doktype'] ?? 0), $this->excludedDoktypes, true)) { |
1170
|
|
|
return false; |
1171
|
|
|
} |
1172
|
|
|
// PageID should not be banned |
1173
|
|
|
if (in_array((int)($data['uid'] ?? 0), $banUidArray, true)) { |
1174
|
|
|
return false; |
1175
|
|
|
} |
1176
|
|
|
// If the page is hide in menu, but the menu does not include them do not show the page |
1177
|
|
|
if (($data['nav_hide'] ?? false) && !($this->conf['includeNotInMenu'] ?? false)) { |
1178
|
|
|
return false; |
1179
|
|
|
} |
1180
|
|
|
// Checking if a page should be shown in the menu depending on whether a translation exists or if the default language is disabled |
1181
|
|
|
if (!$this->sys_page->isPageSuitableForLanguage($data, $this->getCurrentLanguageAspect())) { |
1182
|
|
|
return false; |
1183
|
|
|
} |
1184
|
|
|
// Checking if "&L" should be modified so links to non-accessible pages will not happen. |
1185
|
|
|
if ($this->getCurrentLanguageAspect()->getId() > 0 && $this->conf['protectLvar']) { |
1186
|
|
|
$pageTranslationVisibility = new PageTranslationVisibility((int)($data['l18n_cfg'] ?? 0)); |
1187
|
|
|
if ($this->conf['protectLvar'] === 'all' || $pageTranslationVisibility->shouldHideTranslationIfNoTranslatedRecordExists()) { |
1188
|
|
|
$olRec = $this->sys_page->getPageOverlay($data['uid'], $this->getCurrentLanguageAspect()->getId()); |
1189
|
|
|
if (empty($olRec)) { |
1190
|
|
|
// If no page translation record then page can NOT be accessed in |
1191
|
|
|
// the language pointed to by "&L" and therefore we protect the link by setting "&L=0" |
1192
|
|
|
$data['_ADD_GETVARS'] .= '&L=0'; |
1193
|
|
|
} |
1194
|
|
|
} |
1195
|
|
|
} |
1196
|
|
|
return true; |
1197
|
|
|
} |
1198
|
|
|
|
1199
|
|
|
/** |
1200
|
|
|
* Generating the per-menu-item configuration arrays based on the settings for item states (NO, ACT, CUR etc) |
1201
|
|
|
* set in ->mconf (config for the current menu object) |
1202
|
|
|
* Basically it will produce an individual array for each menu item based on the item states. |
1203
|
|
|
* BUT in addition the "optionSplit" syntax for the values is ALSO evaluated here so that all property-values |
1204
|
|
|
* are "option-splitted" and the output will thus be resolved. |
1205
|
|
|
* Is called from the "generate" functions in the extension classes. The function is processor intensive due to |
1206
|
|
|
* the option split feature in particular. But since the generate function is not always called |
1207
|
|
|
* (since the ->result array may be cached, see makeMenu) it doesn't hurt so badly. |
1208
|
|
|
* |
1209
|
|
|
* @param int $splitCount Number of menu items in the menu |
1210
|
|
|
* @return array the resolved configuration for each item |
1211
|
|
|
*/ |
1212
|
|
|
protected function processItemStates($splitCount) |
1213
|
|
|
{ |
1214
|
|
|
// Prepare normal settings |
1215
|
|
|
if (!is_array($this->mconf['NO.']) && $this->mconf['NO']) { |
1216
|
|
|
// Setting a blank array if NO=1 and there are no properties. |
1217
|
|
|
$this->mconf['NO.'] = []; |
1218
|
|
|
} |
1219
|
|
|
$typoScriptService = GeneralUtility::makeInstance(TypoScriptService::class); |
1220
|
|
|
$NOconf = $typoScriptService->explodeConfigurationForOptionSplit((array)$this->mconf['NO.'], $splitCount); |
1221
|
|
|
|
1222
|
|
|
// Prepare custom states settings, overriding normal settings |
1223
|
|
|
foreach (self::customItemStates as $state) { |
1224
|
|
|
if (empty($this->mconf[$state])) { |
1225
|
|
|
continue; |
1226
|
|
|
} |
1227
|
|
|
$customConfiguration = null; |
1228
|
|
|
foreach ($NOconf as $key => $val) { |
1229
|
|
|
if ($this->isItemState($state, $key)) { |
1230
|
|
|
// if this is the first element of type $state, we must generate the custom configuration. |
1231
|
|
|
if ($customConfiguration === null) { |
1232
|
|
|
$customConfiguration = $typoScriptService->explodeConfigurationForOptionSplit((array)$this->mconf[$state . '.'], $splitCount); |
1233
|
|
|
} |
1234
|
|
|
// Substitute normal with the custom (e.g. IFSUB) |
1235
|
|
|
if (isset($customConfiguration[$key])) { |
1236
|
|
|
$NOconf[$key] = $customConfiguration[$key]; |
1237
|
|
|
} |
1238
|
|
|
} |
1239
|
|
|
} |
1240
|
|
|
} |
1241
|
|
|
|
1242
|
|
|
return $NOconf; |
1243
|
|
|
} |
1244
|
|
|
|
1245
|
|
|
/** |
1246
|
|
|
* Creates the URL, target and onclick values for the menu item link. Returns them in an array as key/value pairs for <A>-tag attributes |
1247
|
|
|
* This function doesn't care about the url, because if we let the url be redirected, it will be logged in the stat!!! |
1248
|
|
|
* |
1249
|
|
|
* @param int $key Pointer to a key in the $this->menuArr array where the value for that key represents the menu item we are linking to (page record) |
1250
|
|
|
* @param string $altTarget Alternative target |
1251
|
|
|
* @param string $typeOverride Alternative type |
1252
|
|
|
* @return array Returns an array with A-tag attributes as key/value pairs (HREF, TARGET and onClick) |
1253
|
|
|
*/ |
1254
|
|
|
protected function link($key, $altTarget, $typeOverride) |
1255
|
|
|
{ |
1256
|
|
|
$runtimeCache = $this->getRuntimeCache(); |
1257
|
|
|
$MP_var = $this->getMPvar($key); |
1258
|
|
|
$cacheId = 'menu-generated-links-' . md5($key . $altTarget . $typeOverride . $MP_var . json_encode($this->menuArr[$key])); |
1259
|
|
|
$runtimeCachedLink = $runtimeCache->get($cacheId); |
1260
|
|
|
if ($runtimeCachedLink !== false) { |
1261
|
|
|
return $runtimeCachedLink; |
1262
|
|
|
} |
1263
|
|
|
|
1264
|
|
|
$tsfe = $this->getTypoScriptFrontendController(); |
1265
|
|
|
|
1266
|
|
|
// If a user script returned the value overrideId in the menu array we use that as page id |
1267
|
|
|
if (($this->mconf['overrideId'] ?? false) || ($this->menuArr[$key]['overrideId'] ?? false)) { |
1268
|
|
|
$overrideId = (int)($this->mconf['overrideId'] ?: $this->menuArr[$key]['overrideId']); |
1269
|
|
|
$overrideId = $overrideId > 0 ? $overrideId : null; |
1270
|
|
|
// Clear MP parameters since ID was changed. |
1271
|
|
|
$MP_params = ''; |
1272
|
|
|
} else { |
1273
|
|
|
$overrideId = null; |
1274
|
|
|
// Mount points: |
1275
|
|
|
$MP_params = $MP_var ? '&MP=' . rawurlencode($MP_var) : ''; |
1276
|
|
|
} |
1277
|
|
|
// Setting main target: |
1278
|
|
|
if ($altTarget) { |
1279
|
|
|
$mainTarget = $altTarget; |
1280
|
|
|
} else { |
1281
|
|
|
$mainTarget = (string)$this->parent_cObj->stdWrapValue('target', $this->mconf ?? []); |
1282
|
|
|
} |
1283
|
|
|
// Creating link: |
1284
|
|
|
$addParams = ($this->mconf['addParams'] ?? '') . $MP_params; |
1285
|
|
|
if (($this->mconf['collapse'] ?? false) && $this->isActive($this->menuArr[$key]['uid'], $this->getMPvar($key))) { |
1286
|
|
|
$thePage = $this->sys_page->getPage($this->menuArr[$key]['pid']); |
1287
|
|
|
$addParams .= $this->menuArr[$key]['_ADD_GETVARS']; |
1288
|
|
|
$LD = $this->menuTypoLink($thePage, $mainTarget, $addParams, $typeOverride, $overrideId); |
1289
|
|
|
} else { |
1290
|
|
|
$addParams .= ($this->I['val']['additionalParams'] ?? '') . ($this->menuArr[$key]['_ADD_GETVARS'] ?? ''); |
1291
|
|
|
$LD = $this->menuTypoLink($this->menuArr[$key], $mainTarget, $addParams, $typeOverride, $overrideId); |
1292
|
|
|
} |
1293
|
|
|
// Override default target configuration if option is set |
1294
|
|
|
if ($this->menuArr[$key]['target'] ?? false) { |
1295
|
|
|
$LD['target'] = $this->menuArr[$key]['target']; |
1296
|
|
|
} |
1297
|
|
|
// Override URL if using "External URL" |
1298
|
|
|
if ((int)($this->menuArr[$key]['doktype'] ?? 0) === PageRepository::DOKTYPE_LINK) { |
1299
|
|
|
$externalUrl = (string)$this->sys_page->getExtURL($this->menuArr[$key]); |
1300
|
|
|
// Create link using typolink (concerning spamProtectEmailAddresses) for email links |
1301
|
|
|
$LD['totalURL'] = $this->parent_cObj->typoLink_URL(['parameter' => $externalUrl]); |
1302
|
|
|
// Links to emails should not have any target |
1303
|
|
|
if (stripos($externalUrl, 'mailto:') === 0) { |
1304
|
|
|
$LD['target'] = ''; |
1305
|
|
|
// use external target for the URL |
1306
|
|
|
} elseif (empty($LD['target']) && !empty($tsfe->extTarget)) { |
1307
|
|
|
$LD['target'] = $tsfe->extTarget; |
1308
|
|
|
} |
1309
|
|
|
} |
1310
|
|
|
|
1311
|
|
|
// Override url if current page is a shortcut |
1312
|
|
|
$shortcut = null; |
1313
|
|
|
if ((int)($this->menuArr[$key]['doktype'] ?? 0) === PageRepository::DOKTYPE_SHORTCUT && (int)$this->menuArr[$key]['shortcut_mode'] !== PageRepository::SHORTCUT_MODE_RANDOM_SUBPAGE) { |
1314
|
|
|
$menuItem = $this->menuArr[$key]; |
1315
|
|
|
try { |
1316
|
|
|
$shortcut = $tsfe->sys_page->getPageShortcut( |
1317
|
|
|
$menuItem['shortcut'], |
1318
|
|
|
$menuItem['shortcut_mode'], |
1319
|
|
|
$menuItem['uid'], |
1320
|
|
|
20, |
1321
|
|
|
[], |
1322
|
|
|
true |
1323
|
|
|
); |
1324
|
|
|
if (isset($menuItem['_PAGES_OVERLAY_LANGUAGE'])) { |
1325
|
|
|
$shortcut = $tsfe->sys_page->getPageOverlay($shortcut, $menuItem['_PAGES_OVERLAY_LANGUAGE']); |
1326
|
|
|
} |
1327
|
|
|
} catch (\Exception $ex) { |
|
|
|
|
1328
|
|
|
} |
1329
|
|
|
if (!is_array($shortcut)) { |
1330
|
|
|
$runtimeCache->set($cacheId, []); |
1331
|
|
|
return []; |
1332
|
|
|
} |
1333
|
|
|
// Only setting url, not target |
1334
|
|
|
$LD['totalURL'] = $this->parent_cObj->typoLink_URL([ |
1335
|
|
|
'parameter' => $shortcut['uid'], |
1336
|
|
|
'language' => $shortcut['_PAGES_OVERLAY_REQUESTEDLANGUAGE'] ?? 'current', |
1337
|
|
|
'additionalParams' => $addParams . $this->I['val']['additionalParams'] . $menuItem['_ADD_GETVARS'], |
1338
|
|
|
'linkAccessRestrictedPages' => !empty($this->mconf['showAccessRestrictedPages']) |
1339
|
|
|
]); |
1340
|
|
|
} |
1341
|
|
|
if ($shortcut) { |
1342
|
|
|
$pageData = $shortcut; |
1343
|
|
|
$pageData['_SHORTCUT_PAGE_UID'] = $this->menuArr[$key]['uid']; |
1344
|
|
|
} else { |
1345
|
|
|
$pageData = $this->menuArr[$key]; |
1346
|
|
|
} |
1347
|
|
|
// Manipulation in case of access restricted pages: |
1348
|
|
|
$this->changeLinksForAccessRestrictedPages($LD, $pageData, $mainTarget, $typeOverride); |
1349
|
|
|
// Overriding URL / Target if set to do so: |
1350
|
|
|
if ($this->menuArr[$key]['_OVERRIDE_HREF'] ?? false) { |
1351
|
|
|
$LD['totalURL'] = $this->menuArr[$key]['_OVERRIDE_HREF']; |
1352
|
|
|
if ($this->menuArr[$key]['_OVERRIDE_TARGET']) { |
1353
|
|
|
$LD['target'] = $this->menuArr[$key]['_OVERRIDE_TARGET']; |
1354
|
|
|
} |
1355
|
|
|
} |
1356
|
|
|
// OnClick open in windows. |
1357
|
|
|
$onClick = ''; |
1358
|
|
|
if ($this->mconf['JSWindow'] ?? false) { |
1359
|
|
|
$conf = $this->mconf['JSWindow.']; |
1360
|
|
|
$url = $LD['totalURL']; |
1361
|
|
|
$LD['totalURL'] = '#'; |
1362
|
|
|
$onClick = 'openPic(' |
1363
|
|
|
. GeneralUtility::quoteJSvalue($tsfe->baseUrlWrap($url)) . ',' |
1364
|
|
|
. '\'' . ($conf['newWindow'] ? md5($url) : 'theNewPage') . '\',' |
1365
|
|
|
. GeneralUtility::quoteJSvalue($conf['params']) . '); return false;'; |
1366
|
|
|
GeneralUtility::makeInstance(AssetCollector::class)->addInlineJavaScript('openPic', 'function openPic(url, winName, winParams) { var theWindow = window.open(url, winName, winParams); if (theWindow) { theWindow.focus(); } }'); |
1367
|
|
|
} |
1368
|
|
|
// look for type and popup |
1369
|
|
|
// following settings are valid in field target: |
1370
|
|
|
// 230 will add type=230 to the link |
1371
|
|
|
// 230 500x600 will add type=230 to the link and open in popup window with 500x600 pixels |
1372
|
|
|
// 230 _blank will add type=230 to the link and open with target "_blank" |
1373
|
|
|
// 230x450:resizable=0,location=1 will open in popup window with 500x600 pixels with settings "resizable=0,location=1" |
1374
|
|
|
$matches = []; |
1375
|
|
|
$targetIsType = $LD['target'] && MathUtility::canBeInterpretedAsInteger($LD['target']) ? (int)$LD['target'] : false; |
1376
|
|
|
if (preg_match('/([0-9]+[\\s])?(([0-9]+)x([0-9]+))?(:.+)?/s', $LD['target'], $matches) || $targetIsType) { |
|
|
|
|
1377
|
|
|
// has type? |
1378
|
|
|
if ((int)($matches[1] ?? 0) || $targetIsType) { |
|
|
|
|
1379
|
|
|
$LD['totalURL'] .= (strpos($LD['totalURL'], '?') === false ? '?' : '&') . 'type=' . ($targetIsType ?: (int)$matches[1]); |
1380
|
|
|
$LD['target'] = $targetIsType ? '' : trim(substr($LD['target'], strlen($matches[1]) + 1)); |
1381
|
|
|
} |
1382
|
|
|
// Open in popup window? |
1383
|
|
|
if (($matches[3] ?? false) && ($matches[4] ?? false)) { |
1384
|
|
|
$target = $LD['target'] ?? 'FEopenLink'; |
1385
|
|
|
$JSparamWH = 'width=' . $matches[3] . ',height=' . $matches[4] . ($matches[5] ? ',' . substr($matches[5], 1) : ''); |
1386
|
|
|
$onClick = 'vHWin=window.open(' |
1387
|
|
|
. GeneralUtility::quoteJSvalue($tsfe->baseUrlWrap($LD['totalURL'])) |
1388
|
|
|
. ',' . GeneralUtility::quoteJSvalue($target) . ',' . GeneralUtility::quoteJSvalue($JSparamWH) . ');vHWin.focus();return false;'; |
1389
|
|
|
$LD['target'] = ''; |
1390
|
|
|
} |
1391
|
|
|
} |
1392
|
|
|
// out: |
1393
|
|
|
$list = []; |
1394
|
|
|
// Added this check: What it does is to enter the baseUrl (if set, which it should for "realurl" based sites) |
1395
|
|
|
// as URL if the calculated value is empty. The problem is that no link is generated with a blank URL |
1396
|
|
|
// and blank URLs might appear when the realurl encoding is used and a link to the frontpage is generated. |
1397
|
|
|
$list['HREF'] = (string)$LD['totalURL'] !== '' ? $LD['totalURL'] : $tsfe->baseUrl; |
1398
|
|
|
$list['TARGET'] = $LD['target']; |
1399
|
|
|
$list['onClick'] = $onClick; |
1400
|
|
|
$runtimeCache->set($cacheId, $list); |
1401
|
|
|
return $list; |
1402
|
|
|
} |
1403
|
|
|
|
1404
|
|
|
/** |
1405
|
|
|
* Determines original shortcut destination in page overlays. |
1406
|
|
|
* |
1407
|
|
|
* Since the pages records used for menu rendering are overlaid by default, |
1408
|
|
|
* the original 'shortcut' value is lost, if a translation did not define one. |
1409
|
|
|
* |
1410
|
|
|
* @param array $page |
1411
|
|
|
* @return array |
1412
|
|
|
*/ |
1413
|
|
|
protected function determineOriginalShortcutPage(array $page) |
1414
|
|
|
{ |
1415
|
|
|
// Check if modification is required |
1416
|
|
|
if ( |
1417
|
|
|
$this->getCurrentLanguageAspect()->getId() > 0 |
1418
|
|
|
&& empty($page['shortcut']) |
1419
|
|
|
&& !empty($page['uid']) |
1420
|
|
|
&& !empty($page['_PAGES_OVERLAY']) |
1421
|
|
|
&& !empty($page['_PAGES_OVERLAY_UID']) |
1422
|
|
|
) { |
1423
|
|
|
// Using raw record since the record was overlaid and is correct already: |
1424
|
|
|
$originalPage = $this->sys_page->getRawRecord('pages', $page['uid']); |
1425
|
|
|
|
1426
|
|
|
if ($originalPage['shortcut_mode'] === $page['shortcut_mode'] && !empty($originalPage['shortcut'])) { |
1427
|
|
|
$page['shortcut'] = $originalPage['shortcut']; |
1428
|
|
|
} |
1429
|
|
|
} |
1430
|
|
|
|
1431
|
|
|
return $page; |
1432
|
|
|
} |
1433
|
|
|
|
1434
|
|
|
/** |
1435
|
|
|
* Will change $LD (passed by reference) if the page is access restricted |
1436
|
|
|
* |
1437
|
|
|
* @param array $LD The array from the linkData() function |
1438
|
|
|
* @param array $page Page array |
1439
|
|
|
* @param string $mainTarget Main target value |
1440
|
|
|
* @param string $typeOverride Type number override if any |
1441
|
|
|
*/ |
1442
|
|
|
protected function changeLinksForAccessRestrictedPages(&$LD, $page, $mainTarget, $typeOverride) |
1443
|
|
|
{ |
1444
|
|
|
// If access restricted pages should be shown in menus, change the link of such pages to link to a redirection page: |
1445
|
|
|
if ($this->mconf['showAccessRestrictedPages'] ?? false && $this->mconf['showAccessRestrictedPages'] !== 'NONE' && !$this->getTypoScriptFrontendController()->checkPageGroupAccess($page)) { |
1446
|
|
|
$thePage = $this->sys_page->getPage($this->mconf['showAccessRestrictedPages']); |
1447
|
|
|
$addParams = str_replace( |
1448
|
|
|
[ |
1449
|
|
|
'###RETURN_URL###', |
1450
|
|
|
'###PAGE_ID###' |
1451
|
|
|
], |
1452
|
|
|
[ |
1453
|
|
|
rawurlencode($LD['totalURL']), |
1454
|
|
|
$page['_SHORTCUT_PAGE_UID'] ?? $page['uid'] |
1455
|
|
|
], |
1456
|
|
|
$this->mconf['showAccessRestrictedPages.']['addParams'] |
1457
|
|
|
); |
1458
|
|
|
$LD = $this->menuTypoLink($thePage, $mainTarget, $addParams, $typeOverride); |
1459
|
|
|
} |
1460
|
|
|
} |
1461
|
|
|
|
1462
|
|
|
/** |
1463
|
|
|
* Creates a submenu level to the current level - if configured for. |
1464
|
|
|
* |
1465
|
|
|
* @param int $uid Page id of the current page for which a submenu MAY be produced (if conditions are met) |
1466
|
|
|
* @param string $objSuffix Object prefix, see ->start() |
1467
|
|
|
* @return string HTML content of the submenu |
1468
|
|
|
*/ |
1469
|
|
|
protected function subMenu(int $uid, string $objSuffix) |
1470
|
|
|
{ |
1471
|
|
|
// Setting alternative menu item array if _SUB_MENU has been defined in the current ->menuArr |
1472
|
|
|
$altArray = ''; |
1473
|
|
|
if (is_array($this->menuArr[$this->I['key']]['_SUB_MENU'] ?? null) && !empty($this->menuArr[$this->I['key']]['_SUB_MENU'])) { |
1474
|
|
|
$altArray = $this->menuArr[$this->I['key']]['_SUB_MENU']; |
1475
|
|
|
} |
1476
|
|
|
// Make submenu if the page is the next active |
1477
|
|
|
$menuType = $this->conf[($this->menuNumber + 1) . $objSuffix] ?? ''; |
1478
|
|
|
// stdWrap for expAll |
1479
|
|
|
$this->mconf['expAll'] = $this->parent_cObj->stdWrapValue('expAll', $this->mconf ?? []); |
1480
|
|
|
if (($this->mconf['expAll'] || $this->isNext($uid, $this->getMPvar($this->I['key'])) || is_array($altArray)) && !($this->mconf['sectionIndex'] ?? false)) { |
1481
|
|
|
try { |
1482
|
|
|
$menuObjectFactory = GeneralUtility::makeInstance(MenuContentObjectFactory::class); |
1483
|
|
|
/** @var AbstractMenuContentObject $submenu */ |
1484
|
|
|
$submenu = $menuObjectFactory->getMenuObjectByType($menuType); |
1485
|
|
|
$submenu->entryLevel = $this->entryLevel + 1; |
1486
|
|
|
$submenu->rL_uidRegister = $this->rL_uidRegister; |
1487
|
|
|
$submenu->MP_array = $this->MP_array; |
1488
|
|
|
if ($this->menuArr[$this->I['key']]['_MP_PARAM'] ?? false) { |
1489
|
|
|
$submenu->MP_array[] = $this->menuArr[$this->I['key']]['_MP_PARAM']; |
1490
|
|
|
} |
1491
|
|
|
// Especially scripts that build the submenu needs the parent data |
1492
|
|
|
$submenu->parent_cObj = $this->parent_cObj; |
1493
|
|
|
$submenu->setParentMenu($this->menuArr, $this->I['key']); |
1494
|
|
|
// Setting alternativeMenuTempArray (will be effective only if an array and not empty) |
1495
|
|
|
if (is_array($altArray) && !empty($altArray)) { |
1496
|
|
|
$submenu->alternativeMenuTempArray = $altArray; |
1497
|
|
|
} |
1498
|
|
|
if ($submenu->start($this->tmpl, $this->sys_page, $uid, $this->conf, $this->menuNumber + 1, $objSuffix)) { |
1499
|
|
|
$submenu->makeMenu(); |
1500
|
|
|
// Memorize the current menu item count |
1501
|
|
|
$tsfe = $this->getTypoScriptFrontendController(); |
1502
|
|
|
$tempCountMenuObj = $tsfe->register['count_MENUOBJ']; |
1503
|
|
|
// Reset the menu item count for the submenu |
1504
|
|
|
$tsfe->register['count_MENUOBJ'] = 0; |
1505
|
|
|
$content = $submenu->writeMenu(); |
1506
|
|
|
// Restore the item count now that the submenu has been handled |
1507
|
|
|
$tsfe->register['count_MENUOBJ'] = $tempCountMenuObj; |
1508
|
|
|
$tsfe->register['count_menuItems'] = count($this->menuArr); |
1509
|
|
|
return $content; |
1510
|
|
|
} |
1511
|
|
|
} catch (NoSuchMenuTypeException $e) { |
|
|
|
|
1512
|
|
|
} |
1513
|
|
|
} |
1514
|
|
|
return ''; |
1515
|
|
|
} |
1516
|
|
|
|
1517
|
|
|
/** |
1518
|
|
|
* Returns TRUE if the page with UID $uid is the NEXT page in root line (which means a submenu should be drawn) |
1519
|
|
|
* |
1520
|
|
|
* @param int $uid Page uid to evaluate. |
1521
|
|
|
* @param string $MPvar MPvar for the current position of item. |
1522
|
|
|
* @return bool TRUE if page with $uid is active |
1523
|
|
|
* @see subMenu() |
1524
|
|
|
*/ |
1525
|
|
|
protected function isNext($uid, $MPvar) |
1526
|
|
|
{ |
1527
|
|
|
// Check for always active PIDs: |
1528
|
|
|
if (in_array((int)$uid, $this->alwaysActivePIDlist, true)) { |
1529
|
|
|
return true; |
1530
|
|
|
} |
1531
|
|
|
$testUid = $uid . ($MPvar ? ':' . $MPvar : ''); |
1532
|
|
|
if ($uid && $testUid == $this->nextActive) { |
1533
|
|
|
return true; |
1534
|
|
|
} |
1535
|
|
|
return false; |
1536
|
|
|
} |
1537
|
|
|
|
1538
|
|
|
/** |
1539
|
|
|
* Returns TRUE if the page with UID $uid is active (in the current rootline) |
1540
|
|
|
* |
1541
|
|
|
* @param int $uid Page uid to evaluate. |
1542
|
|
|
* @param string $MPvar MPvar for the current position of item. |
1543
|
|
|
* @return bool TRUE if page with $uid is active |
1544
|
|
|
*/ |
1545
|
|
|
protected function isActive($uid, $MPvar) |
1546
|
|
|
{ |
1547
|
|
|
// Check for always active PIDs: |
1548
|
|
|
if (in_array((int)$uid, $this->alwaysActivePIDlist, true)) { |
1549
|
|
|
return true; |
1550
|
|
|
} |
1551
|
|
|
$testUid = $uid . ($MPvar ? ':' . $MPvar : ''); |
1552
|
|
|
if ($uid && in_array('ITEM:' . $testUid, $this->rL_uidRegister, true)) { |
1553
|
|
|
return true; |
1554
|
|
|
} |
1555
|
|
|
return false; |
1556
|
|
|
} |
1557
|
|
|
|
1558
|
|
|
/** |
1559
|
|
|
* Returns TRUE if the page with UID $uid is the CURRENT page (equals $this->getTypoScriptFrontendController()->id) |
1560
|
|
|
* |
1561
|
|
|
* @param int $uid Page uid to evaluate. |
1562
|
|
|
* @param string $MPvar MPvar for the current position of item. |
1563
|
|
|
* @return bool TRUE if page $uid = $this->getTypoScriptFrontendController()->id |
1564
|
|
|
*/ |
1565
|
|
|
protected function isCurrent($uid, $MPvar) |
1566
|
|
|
{ |
1567
|
|
|
$testUid = $uid . ($MPvar ? ':' . $MPvar : ''); |
1568
|
|
|
return $uid && end($this->rL_uidRegister) === 'ITEM:' . $testUid; |
1569
|
|
|
} |
1570
|
|
|
|
1571
|
|
|
/** |
1572
|
|
|
* Returns TRUE if there is a submenu with items for the page id, $uid |
1573
|
|
|
* Used by the item states "IFSUB", "ACTIFSUB" and "CURIFSUB" to check if there is a submenu |
1574
|
|
|
* |
1575
|
|
|
* @param int $uid Page uid for which to search for a submenu |
1576
|
|
|
* @return bool Returns TRUE if there was a submenu with items found |
1577
|
|
|
*/ |
1578
|
|
|
protected function isSubMenu($uid) |
1579
|
|
|
{ |
1580
|
|
|
$cacheId = 'menucontentobject-is-submenu-decision-' . $uid; |
1581
|
|
|
$runtimeCache = $this->getRuntimeCache(); |
1582
|
|
|
$cachedDecision = $runtimeCache->get($cacheId); |
1583
|
|
|
if (isset($cachedDecision['result'])) { |
1584
|
|
|
return $cachedDecision['result']; |
1585
|
|
|
} |
1586
|
|
|
// Looking for a mount-pid for this UID since if that |
1587
|
|
|
// exists we should look for a subpages THERE and not in the input $uid; |
1588
|
|
|
$mount_info = $this->sys_page->getMountPointInfo($uid); |
1589
|
|
|
if (is_array($mount_info)) { |
1590
|
|
|
$uid = $mount_info['mount_pid']; |
1591
|
|
|
} |
1592
|
|
|
$recs = $this->sys_page->getMenu($uid, 'uid,pid,doktype,mount_pid,mount_pid_ol,nav_hide,shortcut,shortcut_mode,l18n_cfg'); |
1593
|
|
|
$hasSubPages = false; |
1594
|
|
|
$bannedUids = $this->getBannedUids(); |
1595
|
|
|
$languageId = $this->getCurrentLanguageAspect()->getId(); |
1596
|
|
|
foreach ($recs as $theRec) { |
1597
|
|
|
// no valid subpage if the document type is excluded from the menu |
1598
|
|
|
if (in_array((int)($theRec['doktype'] ?? 0), $this->excludedDoktypes, true)) { |
1599
|
|
|
continue; |
1600
|
|
|
} |
1601
|
|
|
// No valid subpage if the page is hidden inside menus and |
1602
|
|
|
// it wasn't forced to show such entries |
1603
|
|
|
if (isset($theRec['nav_hide']) && $theRec['nav_hide'] |
1604
|
|
|
&& (!isset($this->conf['includeNotInMenu']) || !$this->conf['includeNotInMenu']) |
1605
|
|
|
) { |
1606
|
|
|
continue; |
1607
|
|
|
} |
1608
|
|
|
// No valid subpage if the default language should be shown and the page settings |
1609
|
|
|
// are excluding the visibility of the default language |
1610
|
|
|
$pageTranslationVisibility = new PageTranslationVisibility((int)($theRec['l18n_cfg'] ?? 0)); |
1611
|
|
|
if (!$languageId && $pageTranslationVisibility->shouldBeHiddenInDefaultLanguage()) { |
1612
|
|
|
continue; |
1613
|
|
|
} |
1614
|
|
|
// No valid subpage if the alternative language should be shown and the page settings |
1615
|
|
|
// are requiring a valid overlay but it doesn't exists |
1616
|
|
|
if ($pageTranslationVisibility->shouldHideTranslationIfNoTranslatedRecordExists() && $languageId > 0 && !$theRec['_PAGES_OVERLAY']) { |
1617
|
|
|
continue; |
1618
|
|
|
} |
1619
|
|
|
// No valid subpage if the subpage is banned by excludeUidList |
1620
|
|
|
if (in_array((int)$theRec['uid'], $bannedUids, true)) { |
1621
|
|
|
continue; |
1622
|
|
|
} |
1623
|
|
|
$hasSubPages = true; |
1624
|
|
|
break; |
1625
|
|
|
} |
1626
|
|
|
$runtimeCache->set($cacheId, ['result' => $hasSubPages]); |
1627
|
|
|
return $hasSubPages; |
1628
|
|
|
} |
1629
|
|
|
|
1630
|
|
|
/** |
1631
|
|
|
* Used by processItemStates() to evaluate if a menu item (identified by $key) is in a certain state. |
1632
|
|
|
* |
1633
|
|
|
* @param string $kind The item state to evaluate (SPC, IFSUB, ACT etc...) |
1634
|
|
|
* @param int $key Key pointing to menu item from ->menuArr |
1635
|
|
|
* @return bool Returns TRUE if state matches |
1636
|
|
|
* @see processItemStates() |
1637
|
|
|
*/ |
1638
|
|
|
protected function isItemState($kind, $key) |
1639
|
|
|
{ |
1640
|
|
|
$natVal = false; |
1641
|
|
|
// If any value is set for ITEM_STATE the normal evaluation is discarded |
1642
|
|
|
if ($this->menuArr[$key]['ITEM_STATE'] ?? false) { |
1643
|
|
|
if ((string)$this->menuArr[$key]['ITEM_STATE'] === (string)$kind) { |
1644
|
|
|
$natVal = true; |
1645
|
|
|
} |
1646
|
|
|
} else { |
1647
|
|
|
switch ($kind) { |
1648
|
|
|
case 'SPC': |
1649
|
|
|
$natVal = (bool)$this->menuArr[$key]['isSpacer']; |
1650
|
|
|
break; |
1651
|
|
|
case 'IFSUB': |
1652
|
|
|
$natVal = $this->isSubMenu($this->menuArr[$key]['uid'] ?? 0); |
1653
|
|
|
break; |
1654
|
|
|
case 'ACT': |
1655
|
|
|
$natVal = $this->isActive(($this->menuArr[$key]['uid'] ?? 0), $this->getMPvar($key)); |
1656
|
|
|
break; |
1657
|
|
|
case 'ACTIFSUB': |
1658
|
|
|
$natVal = $this->isActive(($this->menuArr[$key]['uid'] ?? 0), $this->getMPvar($key)) && $this->isSubMenu($this->menuArr[$key]['uid']); |
1659
|
|
|
break; |
1660
|
|
|
case 'CUR': |
1661
|
|
|
$natVal = $this->isCurrent(($this->menuArr[$key]['uid'] ?? 0), $this->getMPvar($key)); |
1662
|
|
|
break; |
1663
|
|
|
case 'CURIFSUB': |
1664
|
|
|
$natVal = $this->isCurrent(($this->menuArr[$key]['uid'] ?? 0), $this->getMPvar($key)) && $this->isSubMenu($this->menuArr[$key]['uid']); |
1665
|
|
|
break; |
1666
|
|
|
case 'USR': |
1667
|
|
|
$natVal = (bool)$this->menuArr[$key]['fe_group']; |
1668
|
|
|
break; |
1669
|
|
|
} |
1670
|
|
|
} |
1671
|
|
|
return $natVal; |
1672
|
|
|
} |
1673
|
|
|
|
1674
|
|
|
/** |
1675
|
|
|
* Creates an access-key for a TMENU menu item based on the menu item titles first letter |
1676
|
|
|
* |
1677
|
|
|
* @param string $title Menu item title. |
1678
|
|
|
* @return array Returns an array with keys "code" ("accesskey" attribute for the img-tag) and "alt" (text-addition to the "alt" attribute) if an access key was defined. Otherwise array was empty |
1679
|
|
|
*/ |
1680
|
|
|
protected function accessKey($title) |
1681
|
|
|
{ |
1682
|
|
|
$tsfe = $this->getTypoScriptFrontendController(); |
1683
|
|
|
// The global array ACCESSKEY is used to globally control if letters are already used!! |
1684
|
|
|
$result = []; |
1685
|
|
|
$title = trim(strip_tags($title)); |
1686
|
|
|
$titleLen = strlen($title); |
1687
|
|
|
for ($a = 0; $a < $titleLen; $a++) { |
1688
|
|
|
$key = strtoupper(substr($title, $a, 1)); |
1689
|
|
|
if (preg_match('/[A-Z]/', $key) && !isset($tsfe->accessKey[$key])) { |
1690
|
|
|
$tsfe->accessKey[$key] = true; |
1691
|
|
|
$result['code'] = ' accesskey="' . $key . '"'; |
1692
|
|
|
$result['alt'] = ' (ALT+' . $key . ')'; |
1693
|
|
|
$result['key'] = $key; |
1694
|
|
|
break; |
1695
|
|
|
} |
1696
|
|
|
} |
1697
|
|
|
return $result; |
1698
|
|
|
} |
1699
|
|
|
|
1700
|
|
|
/** |
1701
|
|
|
* Calls a user function for processing of internal data. |
1702
|
|
|
* Used for the properties "IProcFunc" and "itemArrayProcFunc" |
1703
|
|
|
* |
1704
|
|
|
* @param string $mConfKey Key pointing for the property in the current ->mconf array holding possibly parameters to pass along to the function/method. Currently the keys used are "IProcFunc" and "itemArrayProcFunc". |
1705
|
|
|
* @param mixed $passVar A variable to pass to the user function and which should be returned again from the user function. The idea is that the user function modifies this variable according to what you want to achieve and then returns it. For "itemArrayProcFunc" this variable is $this->menuArr, for "IProcFunc" it is $this->I |
1706
|
|
|
* @return mixed The processed $passVar |
1707
|
|
|
*/ |
1708
|
|
|
protected function userProcess($mConfKey, $passVar) |
1709
|
|
|
{ |
1710
|
|
|
if ($this->mconf[$mConfKey]) { |
1711
|
|
|
$funcConf = (array)($this->mconf[$mConfKey . '.'] ?? []); |
1712
|
|
|
$funcConf['parentObj'] = $this; |
1713
|
|
|
$passVar = $this->parent_cObj->callUserFunction($this->mconf[$mConfKey], $funcConf, $passVar); |
1714
|
|
|
} |
1715
|
|
|
return $passVar; |
1716
|
|
|
} |
1717
|
|
|
|
1718
|
|
|
/** |
1719
|
|
|
* Creates the <A> tag parts for the current item (in $this->I, [A1] and [A2]) based on other information in this array (like $this->I['linkHREF']) |
1720
|
|
|
*/ |
1721
|
|
|
protected function setATagParts() |
1722
|
|
|
{ |
1723
|
|
|
$params = trim($this->I['val']['ATagParams']) . $this->I['accessKey']['code']; |
1724
|
|
|
$params = $params !== '' ? ' ' . $params : ''; |
1725
|
|
|
$this->I['A1'] = '<a ' . GeneralUtility::implodeAttributes($this->I['linkHREF'], true) . $params . '>'; |
1726
|
|
|
$this->I['A2'] = '</a>'; |
1727
|
|
|
} |
1728
|
|
|
|
1729
|
|
|
/** |
1730
|
|
|
* Returns the title for the navigation |
1731
|
|
|
* |
1732
|
|
|
* @param string $title The current page title |
1733
|
|
|
* @param string $nav_title The current value of the navigation title |
1734
|
|
|
* @return string Returns the navigation title if it is NOT blank, otherwise the page title. |
1735
|
|
|
*/ |
1736
|
|
|
protected function getPageTitle($title, $nav_title) |
1737
|
|
|
{ |
1738
|
|
|
return trim($nav_title) !== '' ? $nav_title : $title; |
1739
|
|
|
} |
1740
|
|
|
|
1741
|
|
|
/** |
1742
|
|
|
* Return MPvar string for entry $key in ->menuArr |
1743
|
|
|
* |
1744
|
|
|
* @param int $key Pointer to element in ->menuArr |
1745
|
|
|
* @return string MP vars for element. |
1746
|
|
|
* @see link() |
1747
|
|
|
*/ |
1748
|
|
|
protected function getMPvar($key) |
1749
|
|
|
{ |
1750
|
|
|
if ($GLOBALS['TYPO3_CONF_VARS']['FE']['enable_mount_pids']) { |
1751
|
|
|
$localMP_array = $this->MP_array; |
1752
|
|
|
// NOTICE: "_MP_PARAM" is allowed to be a commalist of PID pairs! |
1753
|
|
|
if ($this->menuArr[$key]['_MP_PARAM'] ?? false) { |
1754
|
|
|
$localMP_array[] = $this->menuArr[$key]['_MP_PARAM']; |
1755
|
|
|
} |
1756
|
|
|
return !empty($localMP_array) ? implode(',', $localMP_array) : ''; |
1757
|
|
|
} |
1758
|
|
|
return ''; |
1759
|
|
|
} |
1760
|
|
|
|
1761
|
|
|
/** |
1762
|
|
|
* Returns where clause part to exclude 'not in menu' pages |
1763
|
|
|
* |
1764
|
|
|
* @return string where clause part. |
1765
|
|
|
*/ |
1766
|
|
|
protected function getDoktypeExcludeWhere() |
1767
|
|
|
{ |
1768
|
|
|
return !empty($this->excludedDoktypes) ? ' AND pages.doktype NOT IN (' . implode(',', $this->excludedDoktypes) . ')' : ''; |
1769
|
|
|
} |
1770
|
|
|
|
1771
|
|
|
/** |
1772
|
|
|
* Returns an array of banned UIDs (from excludeUidList) |
1773
|
|
|
* |
1774
|
|
|
* @return array Array of banned UIDs |
1775
|
|
|
*/ |
1776
|
|
|
protected function getBannedUids() |
1777
|
|
|
{ |
1778
|
|
|
$excludeUidList = (string)$this->parent_cObj->stdWrapValue('excludeUidList', $this->conf ?? []); |
1779
|
|
|
if (!trim($excludeUidList)) { |
1780
|
|
|
return []; |
1781
|
|
|
} |
1782
|
|
|
|
1783
|
|
|
$banUidList = str_replace('current', $this->getTypoScriptFrontendController()->page['uid'] ?? null, $excludeUidList); |
1784
|
|
|
return GeneralUtility::intExplode(',', $banUidList); |
1785
|
|
|
} |
1786
|
|
|
|
1787
|
|
|
/** |
1788
|
|
|
* Calls typolink to create menu item links. |
1789
|
|
|
* |
1790
|
|
|
* @param array $page Page record (uid points where to link to) |
1791
|
|
|
* @param string $oTarget Target frame/window |
1792
|
|
|
* @param string $addParams Parameters to add to URL |
1793
|
|
|
* @param int|string $typeOverride "type" value, empty string means "not set" |
1794
|
|
|
* @param int|null $overridePageId link to this page instead of the $page[uid] value |
1795
|
|
|
* @return array See linkData |
1796
|
|
|
*/ |
1797
|
|
|
protected function menuTypoLink($page, $oTarget, $addParams, $typeOverride, ?int $overridePageId = null) |
1798
|
|
|
{ |
1799
|
|
|
$conf = [ |
1800
|
|
|
'parameter' => $overridePageId ?? $page['uid'] ?? 0 |
1801
|
|
|
]; |
1802
|
|
|
if (MathUtility::canBeInterpretedAsInteger($typeOverride)) { |
1803
|
|
|
$conf['parameter'] .= ',' . (int)$typeOverride; |
1804
|
|
|
} |
1805
|
|
|
if ($addParams) { |
1806
|
|
|
$conf['additionalParams'] = $addParams; |
1807
|
|
|
} |
1808
|
|
|
|
1809
|
|
|
// Ensure that the typolink gets an info which language was actually requested. The $page record could be the record |
1810
|
|
|
// from page translation language=1 as fallback but page translation language=2 was requested. Search for |
1811
|
|
|
// "_PAGES_OVERLAY_REQUESTEDLANGUAGE" for more details |
1812
|
|
|
if (isset($page['_PAGES_OVERLAY_REQUESTEDLANGUAGE'])) { |
1813
|
|
|
$conf['language'] = $page['_PAGES_OVERLAY_REQUESTEDLANGUAGE']; |
1814
|
|
|
} |
1815
|
|
|
if ($oTarget) { |
1816
|
|
|
$conf['target'] = $oTarget; |
1817
|
|
|
} |
1818
|
|
|
if ($page['sectionIndex_uid'] ?? false) { |
1819
|
|
|
$conf['section'] = $page['sectionIndex_uid']; |
1820
|
|
|
} |
1821
|
|
|
$conf['linkAccessRestrictedPages'] = !empty($this->mconf['showAccessRestrictedPages']); |
1822
|
|
|
$this->parent_cObj->typoLink('|', $conf); |
1823
|
|
|
$LD = $this->parent_cObj->lastTypoLinkLD; |
1824
|
|
|
$LD['totalURL'] = $this->parent_cObj->lastTypoLinkUrl; |
1825
|
|
|
return $LD; |
1826
|
|
|
} |
1827
|
|
|
|
1828
|
|
|
/** |
1829
|
|
|
* Generates a list of content objects with sectionIndex enabled |
1830
|
|
|
* available on a specific page |
1831
|
|
|
* |
1832
|
|
|
* Used for menus with sectionIndex enabled |
1833
|
|
|
* |
1834
|
|
|
* @param string $altSortField Alternative sorting field |
1835
|
|
|
* @param int $pid The page id to search for sections |
1836
|
|
|
* @throws \UnexpectedValueException if the query to fetch the content elements unexpectedly fails |
1837
|
|
|
* @return array |
1838
|
|
|
*/ |
1839
|
|
|
protected function sectionIndex($altSortField, $pid = null) |
1840
|
|
|
{ |
1841
|
|
|
$pid = (int)($pid ?: $this->id); |
1842
|
|
|
$basePageRow = $this->sys_page->getPage($pid); |
1843
|
|
|
if (!is_array($basePageRow)) { |
|
|
|
|
1844
|
|
|
return []; |
1845
|
|
|
} |
1846
|
|
|
$useColPos = (int)$this->parent_cObj->stdWrapValue('useColPos', $this->mconf['sectionIndex.'] ?? [], 0); |
1847
|
|
|
$selectSetup = [ |
1848
|
|
|
'pidInList' => $pid, |
1849
|
|
|
'orderBy' => $altSortField, |
1850
|
|
|
'languageField' => 'sys_language_uid', |
1851
|
|
|
'where' => '' |
1852
|
|
|
]; |
1853
|
|
|
|
1854
|
|
|
if ($useColPos >= 0) { |
1855
|
|
|
$expressionBuilder = GeneralUtility::makeInstance(ConnectionPool::class) |
1856
|
|
|
->getConnectionForTable('tt_content') |
1857
|
|
|
->getExpressionBuilder(); |
1858
|
|
|
$selectSetup['where'] = $expressionBuilder->eq('colPos', $useColPos); |
1859
|
|
|
} |
1860
|
|
|
|
1861
|
|
|
if ($basePageRow['content_from_pid'] ?? false) { |
1862
|
|
|
// If the page is configured to show content from a referenced page the sectionIndex contains only contents of |
1863
|
|
|
// the referenced page |
1864
|
|
|
$selectSetup['pidInList'] = $basePageRow['content_from_pid']; |
1865
|
|
|
} |
1866
|
|
|
$statement = $this->parent_cObj->exec_getQuery('tt_content', $selectSetup); |
1867
|
|
|
if (!$statement) { |
|
|
|
|
1868
|
|
|
$message = 'SectionIndex: Query to fetch the content elements failed!'; |
1869
|
|
|
throw new \UnexpectedValueException($message, 1337334849); |
1870
|
|
|
} |
1871
|
|
|
$result = []; |
1872
|
|
|
while ($row = $statement->fetch()) { |
1873
|
|
|
$this->sys_page->versionOL('tt_content', $row); |
1874
|
|
|
if ($this->getCurrentLanguageAspect()->doOverlays() && $basePageRow['_PAGES_OVERLAY_LANGUAGE']) { |
1875
|
|
|
$row = $this->sys_page->getRecordOverlay( |
1876
|
|
|
'tt_content', |
1877
|
|
|
$row, |
1878
|
|
|
$basePageRow['_PAGES_OVERLAY_LANGUAGE'], |
1879
|
|
|
$this->getCurrentLanguageAspect()->getOverlayType() === LanguageAspect::OVERLAYS_MIXED ? '1' : 'hideNonTranslated' |
1880
|
|
|
); |
1881
|
|
|
} |
1882
|
|
|
if ($this->mconf['sectionIndex.']['type'] !== 'all') { |
1883
|
|
|
$doIncludeInSectionIndex = $row['sectionIndex'] >= 1; |
1884
|
|
|
$doHeaderCheck = $this->mconf['sectionIndex.']['type'] === 'header'; |
1885
|
|
|
$isValidHeader = ((int)$row['header_layout'] !== 100 || !empty($this->mconf['sectionIndex.']['includeHiddenHeaders'])) && trim($row['header']) !== ''; |
1886
|
|
|
if (!$doIncludeInSectionIndex || $doHeaderCheck && !$isValidHeader) { |
1887
|
|
|
continue; |
1888
|
|
|
} |
1889
|
|
|
} |
1890
|
|
|
if (is_array($row)) { |
1891
|
|
|
$uid = $row['uid'] ?? null; |
1892
|
|
|
$result[$uid] = $basePageRow; |
1893
|
|
|
$result[$uid]['title'] = $row['header']; |
1894
|
|
|
$result[$uid]['nav_title'] = $row['header']; |
1895
|
|
|
// Prevent false exclusion in filterMenuPages, thus: Always show tt_content records |
1896
|
|
|
$result[$uid]['nav_hide'] = 0; |
1897
|
|
|
$result[$uid]['subtitle'] = $row['subheader'] ?? ''; |
1898
|
|
|
$result[$uid]['starttime'] = $row['starttime'] ?? ''; |
1899
|
|
|
$result[$uid]['endtime'] = $row['endtime'] ?? ''; |
1900
|
|
|
$result[$uid]['fe_group'] = $row['fe_group'] ?? ''; |
1901
|
|
|
$result[$uid]['media'] = $row['media'] ?? ''; |
1902
|
|
|
$result[$uid]['header_layout'] = $row['header_layout'] ?? ''; |
1903
|
|
|
$result[$uid]['bodytext'] = $row['bodytext'] ?? ''; |
1904
|
|
|
$result[$uid]['image'] = $row['image'] ?? ''; |
1905
|
|
|
$result[$uid]['sectionIndex_uid'] = $uid; |
1906
|
|
|
} |
1907
|
|
|
} |
1908
|
|
|
|
1909
|
|
|
return $result; |
1910
|
|
|
} |
1911
|
|
|
|
1912
|
|
|
/** |
1913
|
|
|
* Returns the sys_page object |
1914
|
|
|
* |
1915
|
|
|
* @return PageRepository |
1916
|
|
|
*/ |
1917
|
|
|
public function getSysPage() |
1918
|
|
|
{ |
1919
|
|
|
return $this->sys_page; |
1920
|
|
|
} |
1921
|
|
|
|
1922
|
|
|
/** |
1923
|
|
|
* Returns the parent content object |
1924
|
|
|
* |
1925
|
|
|
* @return ContentObjectRenderer |
1926
|
|
|
*/ |
1927
|
|
|
public function getParentContentObject() |
1928
|
|
|
{ |
1929
|
|
|
return $this->parent_cObj; |
1930
|
|
|
} |
1931
|
|
|
|
1932
|
|
|
/** |
1933
|
|
|
* @return TypoScriptFrontendController |
1934
|
|
|
*/ |
1935
|
|
|
protected function getTypoScriptFrontendController() |
1936
|
|
|
{ |
1937
|
|
|
return $GLOBALS['TSFE']; |
1938
|
|
|
} |
1939
|
|
|
|
1940
|
|
|
protected function getCurrentLanguageAspect(): LanguageAspect |
1941
|
|
|
{ |
1942
|
|
|
return GeneralUtility::makeInstance(Context::class)->getAspect('language'); |
1943
|
|
|
} |
1944
|
|
|
|
1945
|
|
|
/** |
1946
|
|
|
* @return TimeTracker |
1947
|
|
|
*/ |
1948
|
|
|
protected function getTimeTracker() |
1949
|
|
|
{ |
1950
|
|
|
return GeneralUtility::makeInstance(TimeTracker::class); |
1951
|
|
|
} |
1952
|
|
|
|
1953
|
|
|
/** |
1954
|
|
|
* @return \TYPO3\CMS\Core\Cache\Frontend\FrontendInterface |
1955
|
|
|
*/ |
1956
|
|
|
protected function getCache() |
1957
|
|
|
{ |
1958
|
|
|
return GeneralUtility::makeInstance(CacheManager::class)->getCache('hash'); |
1959
|
|
|
} |
1960
|
|
|
|
1961
|
|
|
/** |
1962
|
|
|
* @return \TYPO3\CMS\Core\Cache\Frontend\FrontendInterface |
1963
|
|
|
*/ |
1964
|
|
|
protected function getRuntimeCache() |
1965
|
|
|
{ |
1966
|
|
|
return GeneralUtility::makeInstance(CacheManager::class)->getCache('runtime'); |
1967
|
|
|
} |
1968
|
|
|
|
1969
|
|
|
/** |
1970
|
|
|
* Returns the currently configured "site" if a site is configured (= resolved) in the current request. |
1971
|
|
|
* |
1972
|
|
|
* @return Site |
1973
|
|
|
*/ |
1974
|
|
|
protected function getCurrentSite(): Site |
1975
|
|
|
{ |
1976
|
|
|
return $this->getTypoScriptFrontendController()->getSite(); |
|
|
|
|
1977
|
|
|
} |
1978
|
|
|
|
1979
|
|
|
/** |
1980
|
|
|
* Set the parentMenuArr and key to provide the parentMenu information to the |
1981
|
|
|
* subMenu, special fur IProcFunc and itemArrayProcFunc user functions. |
1982
|
|
|
* |
1983
|
|
|
* @param array $menuArr |
1984
|
|
|
* @param int $menuItemKey |
1985
|
|
|
* @internal |
1986
|
|
|
*/ |
1987
|
|
|
public function setParentMenu(array $menuArr, $menuItemKey) |
1988
|
|
|
{ |
1989
|
|
|
// check if menuArr is a valid array and that menuItemKey matches an existing menuItem in menuArr |
1990
|
|
|
if (is_array($menuArr) |
1991
|
|
|
&& (is_int($menuItemKey) && $menuItemKey >= 0 && isset($menuArr[$menuItemKey])) |
1992
|
|
|
) { |
1993
|
|
|
$this->parentMenuArr = $menuArr; |
1994
|
|
|
$this->parentMenuArrItemKey = $menuItemKey; |
1995
|
|
|
} |
1996
|
|
|
} |
1997
|
|
|
|
1998
|
|
|
/** |
1999
|
|
|
* Check if there is a valid parentMenuArr. |
2000
|
|
|
* |
2001
|
|
|
* @return bool |
2002
|
|
|
*/ |
2003
|
|
|
protected function hasParentMenuArr() |
2004
|
|
|
{ |
2005
|
|
|
return |
2006
|
|
|
$this->menuNumber > 1 |
2007
|
|
|
&& is_array($this->parentMenuArr) |
2008
|
|
|
&& !empty($this->parentMenuArr) |
2009
|
|
|
; |
2010
|
|
|
} |
2011
|
|
|
|
2012
|
|
|
/** |
2013
|
|
|
* Check if we have a parentMenuArrItemKey |
2014
|
|
|
*/ |
2015
|
|
|
protected function hasParentMenuItemKey() |
2016
|
|
|
{ |
2017
|
|
|
return null !== $this->parentMenuArrItemKey; |
2018
|
|
|
} |
2019
|
|
|
|
2020
|
|
|
/** |
2021
|
|
|
* Check if the the parentMenuItem exists |
2022
|
|
|
*/ |
2023
|
|
|
protected function hasParentMenuItem() |
2024
|
|
|
{ |
2025
|
|
|
return |
2026
|
|
|
$this->hasParentMenuArr() |
2027
|
|
|
&& $this->hasParentMenuItemKey() |
2028
|
|
|
&& isset($this->getParentMenuArr()[$this->parentMenuArrItemKey]) |
2029
|
|
|
; |
2030
|
|
|
} |
2031
|
|
|
|
2032
|
|
|
/** |
2033
|
|
|
* Get the parentMenuArr, if this is subMenu. |
2034
|
|
|
* |
2035
|
|
|
* @return array |
2036
|
|
|
*/ |
2037
|
|
|
public function getParentMenuArr() |
2038
|
|
|
{ |
2039
|
|
|
return $this->hasParentMenuArr() ? $this->parentMenuArr : []; |
2040
|
|
|
} |
2041
|
|
|
|
2042
|
|
|
/** |
2043
|
|
|
* Get the parentMenuItem from the parentMenuArr, if this is a subMenu |
2044
|
|
|
* |
2045
|
|
|
* @return array|null |
2046
|
|
|
*/ |
2047
|
|
|
public function getParentMenuItem() |
2048
|
|
|
{ |
2049
|
|
|
// check if we have a parentMenuItem and if it is an array |
2050
|
|
|
if ($this->hasParentMenuItem() |
2051
|
|
|
&& is_array($this->getParentMenuArr()[$this->parentMenuArrItemKey]) |
2052
|
|
|
) { |
2053
|
|
|
return $this->getParentMenuArr()[$this->parentMenuArrItemKey]; |
2054
|
|
|
} |
2055
|
|
|
|
2056
|
|
|
return null; |
2057
|
|
|
} |
2058
|
|
|
|
2059
|
|
|
/** |
2060
|
|
|
* @param string $mode |
2061
|
|
|
* @return string |
2062
|
|
|
*/ |
2063
|
|
|
private function getMode(string $mode = ''): string |
2064
|
|
|
{ |
2065
|
|
|
switch ($mode) { |
2066
|
|
|
case 'starttime': |
2067
|
|
|
$sortField = 'starttime'; |
2068
|
|
|
break; |
2069
|
|
|
case 'lastUpdated': |
2070
|
|
|
case 'manual': |
2071
|
|
|
$sortField = 'lastUpdated'; |
2072
|
|
|
break; |
2073
|
|
|
case 'tstamp': |
2074
|
|
|
$sortField = 'tstamp'; |
2075
|
|
|
break; |
2076
|
|
|
case 'crdate': |
2077
|
|
|
$sortField = 'crdate'; |
2078
|
|
|
break; |
2079
|
|
|
default: |
2080
|
|
|
$sortField = 'SYS_LASTCHANGED'; |
2081
|
|
|
} |
2082
|
|
|
|
2083
|
|
|
return $sortField; |
2084
|
|
|
} |
2085
|
|
|
} |
2086
|
|
|
|