first commit

This commit is contained in:
salam committed 2026-10-19 08:18:39 +02:00
commit 1218dd1517
12716 files changed
+989233

No files matched your search

File diff suppressed because it is too large. Load diff
@@ -0,0 +1,32 @@
<?php
namespace TYPO3\CMS\Frontend\ContentObject\Menu;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* interface for classes which hook into AbstractMenuContentObject
*/
interface AbstractMenuFilterPagesHookInterface
{
/**
* Checks if a page is OK to include in the final menu item array.
*
* @param array $data Array of menu items
* @param array $banUidArray Array of page uids which are to be excluded
* @param bool $spacer If set, then the page is a spacer.
* @param \TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject $obj The menu object
* @return bool Returns TRUE if the page can be safely included.
*/
public function processFilter(array &$data, array $banUidArray, $spacer, AbstractMenuContentObject $obj);
}
@@ -0,0 +1,147 @@
<?php
namespace TYPO3\CMS\Frontend\ContentObject\Menu;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3\CMS\Core\Collection\AbstractRecordCollection;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Frontend\Category\Collection\CategoryCollection;
/**
* Utility class for menus based on category collections of pages.
*
* Returns all the relevant pages for rendering with a menu content object.
* @internal this is only used for internal purposes and solely used for EXT:frontend and not part of TYPO3's Core API.
*/
class CategoryMenuUtility
{
/**
* @var string Name of the field used for sorting the pages
*/
protected static $sortingField;
/**
* Collects all pages for the selected categories, sorted according to configuration.
*
* @param string $selectedCategories Comma-separated list of system categories primary keys
* @param array $configuration TypoScript configuration for the "special." keyword
* @param \TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject $parentObject Back-reference to the calling object
* @return array List of selected pages
*/
public function collectPages($selectedCategories, $configuration, $parentObject)
{
$selectedPages = [];
$categoriesPerPage = [];
// Determine the name of the relation field
$relationField = '';
if (isset($configuration['relation.'])) {
$relationField = $parentObject->parent_cObj->stdWrap(
$configuration['relation'],
$configuration['relation.']
);
} elseif (isset($configuration['relation'])) {
$relationField = $configuration['relation'];
}
// Get the pages for each selected category
$selectedCategories = GeneralUtility::intExplode(',', $selectedCategories, true);
foreach ($selectedCategories as $aCategory) {
$collection = CategoryCollection::load(
$aCategory,
true,
'pages',
$relationField
);
$categoryUid = 0;
if ($collection instanceof AbstractRecordCollection) {
$categoryUid = $collection->getUid();
}
// Loop on the results, overlay each page record found
foreach ($collection as $pageItem) {
$parentObject->getSysPage()->versionOL('pages', $pageItem, true);
if (is_array($pageItem)) {
$selectedPages[$pageItem['uid']] = $parentObject->getSysPage()->getPageOverlay($pageItem);
// Keep a list of the categories each page belongs to
if (!isset($categoriesPerPage[$pageItem['uid']])) {
$categoriesPerPage[$pageItem['uid']] = [];
}
$categoriesPerPage[$pageItem['uid']][] = $categoryUid;
}
}
}
// Loop on the selected pages to add the categories they belong to, as comma-separated list of category uid's)
// (this makes them available for rendering, if needed)
foreach ($selectedPages as $uid => $pageRecord) {
$selectedPages[$uid]['_categories'] = implode(',', $categoriesPerPage[$uid]);
}
// Sort the pages according to the sorting property
self::$sortingField = isset($configuration['sorting.']) ? $parentObject->getParentContentObject()->stdWrap($configuration['sorting'], $configuration['sorting.']) : $configuration['sorting'];
$order = isset($configuration['order.']) ? $parentObject->getParentContentObject()->stdWrap($configuration['order'], $configuration['order.']) : $configuration['order'];
$selectedPages = $this->sortPages($selectedPages, $order);
return $selectedPages;
}
/**
* Sorts the selected pages
*
* If the sorting field is not defined or does not corresponding to an existing field
* of the "pages" tables, the list of pages will remain unchanged.
*
* @param array $pages List of selected pages
* @param string $order Order for sorting (should "asc" or "desc")
* @return array Sorted list of pages
*/
protected function sortPages($pages, $order)
{
// Perform the sorting only if a criterion was actually defined
if (!empty(self::$sortingField)) {
// Check that the sorting field exists (checking the first record is enough)
$firstPage = current($pages);
if (isset($firstPage[self::$sortingField])) {
// Make sure the order property is either "asc" or "desc" (default is "asc")
if (!empty($order)) {
$order = strtolower($order);
if ($order !== 'desc') {
$order = 'asc';
}
}
uasort(
$pages,
[
self::class,
'sortPagesUtility'
]
);
// If the sort order is descending, reverse the sorted array
if ($order === 'desc') {
$pages = array_reverse($pages, true);
}
}
}
return $pages;
}
/**
* Static utility for sorting pages according to the selected criterion
*
* @param array $pageA Record for first page to be compared
* @param array $pageB Record for second page to be compared
* @return array -1 if first argument is smaller than second argument, 1 if first is greater than second and 0 if both are equal
*/
public static function sortPagesUtility($pageA, $pageB)
{
return strnatcasecmp($pageA[self::$sortingField], $pageB[self::$sortingField]);
}
}
@@ -0,0 +1,22 @@
<?php
namespace TYPO3\CMS\Frontend\ContentObject\Menu\Exception;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* No such menu type exception
*/
class NoSuchMenuTypeException extends \TYPO3\CMS\Frontend\Exception
{
}
@@ -0,0 +1,569 @@
<?php
namespace TYPO3\CMS\Frontend\ContentObject\Menu;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3\CMS\Core\Core\Environment;
use TYPO3\CMS\Core\Type\File\ImageInfo;
use TYPO3\CMS\Core\TypoScript\TypoScriptService;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
use TYPO3\CMS\Frontend\Imaging\GifBuilder;
/**
* Extension class creating graphic based menus (PNG or GIF files)
*
* @deprecated since TYPO3 v9.4, will be removed in TYPO3 v10.0
*/
class GraphicalMenuContentObject extends AbstractMenuContentObject
{
public function __construct()
{
trigger_error('GMENU and GraphicalMenuContentObject will be removed in TYPO3 v10.0, you should build accessible websites with TMENU/Text, and optional images on top, which can be achieved with TypoScript.', E_USER_DEPRECATED);
}
/**
* Calls procesItemStates() so that the common configuration for the menu items are resolved into individual configuration per item.
* Calls makeGifs() for all "normal" items and if configured for, also the "rollover" items.
*
* @see AbstractMenuContentObject::procesItemStates(), makeGifs()
*/
public function generate()
{
$splitCount = count($this->menuArr);
if ($splitCount) {
list($NOconf, $ROconf) = $this->procesItemStates($splitCount);
//store initial count value
$tsfe = $this->getTypoScriptFrontendController();
$temp_HMENU_MENUOBJ = $tsfe->register['count_HMENU_MENUOBJ'];
$temp_MENUOBJ = $tsfe->register['count_MENUOBJ'];
// Now we generate the giffiles:
$this->makeGifs($NOconf, 'NO');
// store count from NO obj
$tempcnt_HMENU_MENUOBJ = $tsfe->register['count_HMENU_MENUOBJ'];
$tempcnt_MENUOBJ = $tsfe->register['count_MENUOBJ'];
if ($this->mconf['debugItemConf']) {
echo '<h3>$NOconf:</h3>';
debug($NOconf);
}
// RollOver
if ($ROconf) {
// Start recount for rollover with initial values
$tsfe->register['count_HMENU_MENUOBJ'] = $temp_HMENU_MENUOBJ;
$tsfe->register['count_MENUOBJ'] = $temp_MENUOBJ;
$this->makeGifs($ROconf, 'RO');
if ($this->mconf['debugItemConf']) {
echo '<h3>$ROconf:</h3>';
debug($ROconf);
}
}
// Use count from NO obj
$tsfe->register['count_HMENU_MENUOBJ'] = $tempcnt_HMENU_MENUOBJ;
$tsfe->register['count_MENUOBJ'] = $tempcnt_MENUOBJ;
}
}
/**
* Will traverse input array with configuration per-item and create corresponding GIF files for the menu.
* The data of the files are stored in $this->result
*
* @param array $conf Array with configuration for each item.
* @param string $resKey Type of images: normal ("NO") or rollover ("RO"). Valid values are "NO" and "RO
* @internal
* @see generate()
*/
public function makeGifs($conf, $resKey)
{
$isGD = $GLOBALS['TYPO3_CONF_VARS']['GFX']['gdlib'];
if (!is_array($conf)) {
$conf = [];
}
$totalWH = [];
$items = count($conf);
$minDim = 0;
$maxDim = 0;
$Hcounter = 0;
$Wcounter = 0;
$Hobjs = [];
$Wobjs = [];
if ($isGD) {
// Generate the gif-files. the $menuArr is filled with some values like output_w, output_h, output_file
$Hobjs = $this->mconf['applyTotalH'];
if ($Hobjs) {
$Hobjs = GeneralUtility::intExplode(',', $Hobjs);
}
$Wobjs = $this->mconf['applyTotalW'];
if ($Wobjs) {
$Wobjs = GeneralUtility::intExplode(',', $Wobjs);
}
$minDim = $this->mconf['min'];
if ($minDim) {
$minDim = $this->calcIntExplode($minDim . ',');
}
$maxDim = $this->mconf['max'];
if ($maxDim) {
$maxDim = $this->calcIntExplode($maxDim . ',');
}
if ($minDim) {
$conf[$items] = $conf[$items - 1];
$this->menuArr[$items] = [];
$items = count($conf);
}
// TOTAL width
if ($this->mconf['useLargestItemX'] || $this->mconf['useLargestItemY'] || $this->mconf['distributeX'] || $this->mconf['distributeY']) {
$totalWH = $this->findLargestDims($conf, $items, $Hobjs, $Wobjs, $minDim, $maxDim);
}
}
$c = 0;
$maxFlag = 0;
$distributeAccu = ['H' => 0, 'W' => 0];
foreach ($conf as $key => $val) {
$this->getTypoScriptFrontendController()->register['count_HMENU_MENUOBJ']++;
$this->getTypoScriptFrontendController()->register['count_MENUOBJ']++;
if ($items === $c + 1 && $minDim) {
$Lobjs = $this->mconf['removeObjectsOfDummy'];
if ($Lobjs) {
$Lobjs = GeneralUtility::intExplode(',', $Lobjs);
foreach ($Lobjs as $remItem) {
unset($val[$remItem]);
unset($val[$remItem . '.']);
}
}
$flag = 0;
$tempXY = explode(',', $val['XY']);
if ($Wcounter < $minDim[0]) {
$tempXY[0] = $minDim[0] - $Wcounter;
$flag = 1;
}
if ($Hcounter < $minDim[1]) {
$tempXY[1] = $minDim[1] - $Hcounter;
$flag = 1;
}
$val['XY'] = implode(',', $tempXY);
if (!$flag) {
break;
}
}
$c++;
$gifCreator = null;
if ($isGD) {
// Pre-working the item
$gifCreator = GeneralUtility::makeInstance(GifBuilder::class);
$gifCreator->start($val, $this->menuArr[$key]);
// If useLargestItemH/W is specified
if (!empty($totalWH) && ($this->mconf['useLargestItemX'] || $this->mconf['useLargestItemY'])) {
$tempXY = explode(',', $gifCreator->setup['XY']);
if ($this->mconf['useLargestItemX']) {
$tempXY[0] = max($totalWH['W']);
}
if ($this->mconf['useLargestItemY']) {
$tempXY[1] = max($totalWH['H']);
}
// Regenerate the new values...
$val['XY'] = implode(',', $tempXY);
$gifCreator = GeneralUtility::makeInstance(GifBuilder::class);
$gifCreator->start($val, $this->menuArr[$key]);
}
// If distributeH/W is specified
if (!empty($totalWH) && ($this->mconf['distributeX'] || $this->mconf['distributeY'])) {
$tempXY = explode(',', $gifCreator->setup['XY']);
if ($this->mconf['distributeX']) {
$diff = $this->mconf['distributeX'] - $totalWH['W_total'] - $distributeAccu['W'];
$compensate = round($diff / ($items - $c + 1));
$distributeAccu['W'] += $compensate;
$tempXY[0] = $totalWH['W'][$key] + $compensate;
}
if ($this->mconf['distributeY']) {
$diff = $this->mconf['distributeY'] - $totalWH['H_total'] - $distributeAccu['H'];
$compensate = round($diff / ($items - $c + 1));
$distributeAccu['H'] += $compensate;
$tempXY[1] = $totalWH['H'][$key] + $compensate;
}
// Regenerate the new values...
$val['XY'] = implode(',', $tempXY);
$gifCreator = GeneralUtility::makeInstance(GifBuilder::class);
$gifCreator->start($val, $this->menuArr[$key]);
}
// If max dimensions are specified
if ($maxDim) {
$tempXY = explode(',', $val['XY']);
if ($maxDim[0] && $Wcounter + $gifCreator->XY[0] >= $maxDim[0]) {
$tempXY[0] = $maxDim[0] - $Wcounter;
$maxFlag = 1;
}
if ($maxDim[1] && $Hcounter + $gifCreator->XY[1] >= $maxDim[1]) {
$tempXY[1] = $maxDim[1] - $Hcounter;
$maxFlag = 1;
}
if ($maxFlag) {
$val['XY'] = implode(',', $tempXY);
$gifCreator = GeneralUtility::makeInstance(GifBuilder::class);
$gifCreator->start($val, $this->menuArr[$key]);
}
}
// displace
if ($Hobjs) {
foreach ($Hobjs as $index) {
if ($gifCreator->setup[$index] && $gifCreator->setup[$index . '.']) {
$oldOffset = explode(',', $gifCreator->setup[$index . '.']['offset']);
$gifCreator->setup[$index . '.']['offset'] = implode(',', $gifCreator->applyOffset($oldOffset, [0, -$Hcounter]));
}
}
}
if ($Wobjs) {
foreach ($Wobjs as $index) {
if ($gifCreator->setup[$index] && $gifCreator->setup[$index . '.']) {
$oldOffset = explode(',', $gifCreator->setup[$index . '.']['offset']);
$gifCreator->setup[$index . '.']['offset'] = implode(',', $gifCreator->applyOffset($oldOffset, [-$Wcounter, 0]));
}
}
}
}
// Finding alternative GIF names if any (by altImgResource)
$gifFileName = '';
if ($conf[$key]['altImgResource'] || is_array($conf[$key]['altImgResource.'])) {
$cObj = GeneralUtility::makeInstance(ContentObjectRenderer::class);
$cObj->start($this->menuArr[$key], 'pages');
$altImgInfo = $cObj->getImgResource($conf[$key]['altImgResource'], $conf[$key]['altImgResource.']);
$gifFileName = $altImgInfo[3];
}
// If an alternative name was NOT given, find the GIFBUILDER name.
if (!$gifFileName && $isGD) {
GeneralUtility::mkdir_deep(Environment::getPublicPath() . '/typo3temp/assets/menu/');
$gifFileName = $gifCreator->fileName('assets/menu/');
}
$this->result[$resKey][$key] = $conf[$key];
// Generation of image file:
// File exists
if (file_exists($gifFileName)) {
$imageInfo = GeneralUtility::makeInstance(ImageInfo::class, $gifFileName);
$this->result[$resKey][$key]['output_w'] = (int)$imageInfo->getWidth();
$this->result[$resKey][$key]['output_h'] = (int)$imageInfo->getHeight();
$this->result[$resKey][$key]['output_file'] = $gifFileName;
} elseif ($isGD) {
// file is generated
$gifCreator->make();
$this->result[$resKey][$key]['output_w'] = $gifCreator->w;
$this->result[$resKey][$key]['output_h'] = $gifCreator->h;
$this->result[$resKey][$key]['output_file'] = $gifFileName;
$gifCreator->output($this->result[$resKey][$key]['output_file']);
$gifCreator->destroy();
}
// counter is increased
$Hcounter += $this->result[$resKey][$key]['output_h'];
// counter is increased
$Wcounter += $this->result[$resKey][$key]['output_w'];
if ($maxFlag) {
break;
}
}
}
/**
* Function searching for the largest width and height of the menu items to be generated.
* Uses some of the same code as makeGifs and even instantiates some gifbuilder objects BUT does not render the images - only reading out which width they would have.
* Remember to upgrade the code in here if the makeGifs function is updated.
*
* @param array $conf Same configuration array as passed to makeGifs()
* @param int $items The number of menu items
* @param array $Hobjs Array with "applyTotalH" numbers (unused)
* @param array $Wobjs Array with "applyTotalW" numbers (unused)
* @param array $minDim Array with "min" x/y
* @param array $maxDim Array with "max" x/y
* @return array Array with keys "H" and "W" which are in themselves arrays with the heights and widths of menu items inside. This can be used to find the max/min size of the menu items.
* @internal
* @see makeGifs()
*/
public function findLargestDims($conf, $items, $Hobjs, $Wobjs, $minDim, $maxDim)
{
$items = (int)$items;
$totalWH = [
'W' => [],
'H' => [],
'W_total' => 0,
'H_total' => 0
];
$Hcounter = 0;
$Wcounter = 0;
$c = 0;
$maxFlag = 0;
foreach ($conf as $key => $val) {
// SAME CODE AS makeGifs()! BEGIN
if ($items === $c + 1 && $minDim) {
$Lobjs = $this->mconf['removeObjectsOfDummy'];
if ($Lobjs) {
$Lobjs = GeneralUtility::intExplode(',', $Lobjs);
foreach ($Lobjs as $remItem) {
unset($val[$remItem]);
unset($val[$remItem . '.']);
}
}
$flag = 0;
$tempXY = explode(',', $val['XY']);
if ($Wcounter < $minDim[0]) {
$tempXY[0] = $minDim[0] - $Wcounter;
$flag = 1;
}
if ($Hcounter < $minDim[1]) {
$tempXY[1] = $minDim[1] - $Hcounter;
$flag = 1;
}
$val['XY'] = implode(',', $tempXY);
if (!$flag) {
break;
}
}
$c++;
$gifCreator = GeneralUtility::makeInstance(GifBuilder::class);
$gifCreator->start($val, $this->menuArr[$key]);
if ($maxDim) {
$tempXY = explode(',', $val['XY']);
if ($maxDim[0] && $Wcounter + $gifCreator->XY[0] >= $maxDim[0]) {
$tempXY[0] = $maxDim[0] - $Wcounter;
$maxFlag = 1;
}
if ($maxDim[1] && $Hcounter + $gifCreator->XY[1] >= $maxDim[1]) {
$tempXY[1] = $maxDim[1] - $Hcounter;
$maxFlag = 1;
}
if ($maxFlag) {
$val['XY'] = implode(',', $tempXY);
$gifCreator = GeneralUtility::makeInstance(GifBuilder::class);
$gifCreator->start($val, $this->menuArr[$key]);
}
}
// SAME CODE AS makeGifs()! END
// Setting the width/height
$totalWH['W'][$key] = $gifCreator->XY[0];
$totalWH['H'][$key] = $gifCreator->XY[1];
$totalWH['W_total'] += $gifCreator->XY[0];
$totalWH['H_total'] += $gifCreator->XY[1];
// counter is increased
$Hcounter += $gifCreator->XY[1];
// counter is increased
$Wcounter += $gifCreator->XY[0];
if ($maxFlag) {
break;
}
}
return $totalWH;
}
/**
* Traverses the ->result['NO'] array of menu items configuration (made by ->generate()) and renders the HTML of each item (the images themselves was made with makeGifs() before this. See ->generate())
* During the execution of this function many internal methods prefixed "extProc_" from this class is called and many of these are for now dummy functions. But they can be used for processing as they are used by the GMENU_LAYERS
*
* @return string The HTML for the menu (returns result through $this->extProc_finish(); )
*/
public function writeMenu()
{
if (!is_array($this->menuArr) || empty($this->result) || !is_array($this->result['NO'])) {
return '';
}
$this->WMresult = '';
$this->INPfixMD5 = substr(md5(microtime() . $this->GMENU_fixKey), 0, 4);
$this->WMmenuItems = count($this->result['NO']);
$typoScriptService = GeneralUtility::makeInstance(TypoScriptService::class);
$this->WMsubmenuObjSuffixes = $typoScriptService->explodeConfigurationForOptionSplit(['sOSuffix' => $this->mconf['submenuObjSuffixes']], $this->WMmenuItems);
$this->extProc_init();
$tsfe = $this->getTypoScriptFrontendController();
if (!isset($tsfe->additionalJavaScript['JSImgCode'])) {
$tsfe->additionalJavaScript['JSImgCode'] = '';
}
for ($key = 0; $key < $this->WMmenuItems; $key++) {
if ($this->result['NO'][$key]['output_file']) {
// Initialize the cObj with the page record of the menu item
$this->WMcObj->start($this->menuArr[$key], 'pages');
$this->I = [];
$this->I['key'] = $key;
$this->I['INPfix'] = ($this->imgNameNotRandom ? '' : '_' . $this->INPfixMD5) . '_' . $key;
$this->I['val'] = $this->result['NO'][$key];
$this->I['title'] = $this->getPageTitle($this->menuArr[$key]['title'], $this->menuArr[$key]['nav_title']);
$this->I['uid'] = $this->menuArr[$key]['uid'];
$this->I['mount_pid'] = $this->menuArr[$key]['mount_pid'];
$this->I['pid'] = $this->menuArr[$key]['pid'];
$this->I['spacer'] = $this->menuArr[$key]['isSpacer'];
if (!$this->I['uid'] && !$this->menuArr[$key]['_OVERRIDE_HREF']) {
$this->I['spacer'] = 1;
}
$this->I['noLink'] = $this->I['spacer'] || $this->I['val']['noLink'] || empty($this->menuArr[$key]);
// !count($this->menuArr[$key]) means that this item is a dummyItem
$this->I['name'] = '';
// Set access key
if ($this->mconf['accessKey']) {
$this->I['accessKey'] = $this->accessKey($this->I['title']);
} else {
$this->I['accessKey'] = [];
}
// Make link tag
$this->I['val']['ATagParams'] = $this->WMcObj->getATagParams($this->I['val']);
if (isset($this->I['val']['additionalParams.'])) {
$this->I['val']['additionalParams'] = $this->WMcObj->stdWrap($this->I['val']['additionalParams'], $this->I['val']['additionalParams.']);
}
$this->I['linkHREF'] = $this->link($key, $this->I['val']['altTarget'], $this->mconf['forceTypeValue']);
// Title attribute of links:
$titleAttrValue = isset($this->I['val']['ATagTitle.']) ? $this->WMcObj->stdWrap($this->I['val']['ATagTitle'], $this->I['val']['ATagTitle.']) . $this->I['accessKey']['alt'] : $this->I['val']['ATagTitle'] . $this->I['accessKey']['alt'];
if ($titleAttrValue !== '') {
$this->I['linkHREF']['title'] = $titleAttrValue;
}
// Set rollover
if ($this->result['RO'][$key] && !$this->I['noLink']) {
$this->I['theName'] = $this->imgNamePrefix . $this->I['uid'] . $this->I['INPfix'];
$this->I['name'] = ' ' . $this->nameAttribute . '="' . $this->I['theName'] . '"';
$this->I['linkHREF']['onMouseover'] = $this->WMfreezePrefix . 'over(' . GeneralUtility::quoteJSvalue($this->I['theName']) . ');';
$this->I['linkHREF']['onMouseout'] = $this->WMfreezePrefix . 'out(' . GeneralUtility::quoteJSvalue($this->I['theName']) . ');';
$tsfe->additionalJavaScript['JSImgCode'] .= LF . $this->I['theName'] . '_n=new Image(); ' . $this->I['theName'] . '_n.src = ' . GeneralUtility::quoteJSvalue($tsfe->absRefPrefix . $this->I['val']['output_file']) . '; ';
$tsfe->additionalJavaScript['JSImgCode'] .= LF . $this->I['theName'] . '_h=new Image(); ' . $this->I['theName'] . '_h.src = ' . GeneralUtility::quoteJSvalue($tsfe->absRefPrefix . $this->result['RO'][$key]['output_file']) . '; ';
$tsfe->imagesOnPage[] = $this->result['RO'][$key]['output_file'];
$tsfe->setJS('mouseOver');
$this->extProc_RO($key);
}
// Set altText
$this->I['altText'] = $this->I['title'] . $this->I['accessKey']['alt'];
// Calling extra processing function
$this->extProc_beforeLinking($key);
// Set linking
if (!$this->I['noLink']) {
$this->setATagParts();
} else {
$this->I['A1'] = '';
$this->I['A2'] = '';
}
$this->I['IMG'] = '<img src="' . $tsfe->absRefPrefix . $this->I['val']['output_file'] . '" width="' . $this->I['val']['output_w'] . '" height="' . $this->I['val']['output_h'] . '" ' . $this->parent_cObj->getBorderAttr('border="0"') . ($this->mconf['disableAltText'] ? '' : ' alt="' . htmlspecialchars($this->I['altText']) . '"') . $this->I['name'] . ($this->I['val']['imgParams'] ? ' ' . $this->I['val']['imgParams'] : '') . ' />';
// Make before, middle and after parts
$this->I['parts'] = [];
$this->I['parts']['ATag_begin'] = $this->I['A1'];
$this->I['parts']['image'] = $this->I['IMG'];
$this->I['parts']['ATag_end'] = $this->I['A2'];
// Passing I to a user function
if ($this->mconf['IProcFunc']) {
$this->I = $this->userProcess('IProcFunc', $this->I);
}
// Putting the item together.
// Merge parts + beforeAllWrap
$this->I['theItem'] = implode('', $this->I['parts']);
$this->I['theItem'] = $this->extProc_beforeAllWrap($this->I['theItem'], $key);
// wrap:
$this->I['theItem'] = $this->WMcObj->wrap($this->I['theItem'], $this->I['val']['wrap']);
// allWrap:
$allWrap = isset($this->I['val']['allWrap.']) ? $this->WMcObj->stdWrap($this->I['val']['allWrap'], $this->I['val']['allWrap.']) : $this->I['val']['allWrap'];
$this->I['theItem'] = $this->WMcObj->wrap($this->I['theItem'], $allWrap);
if ($this->I['val']['subst_elementUid']) {
$this->I['theItem'] = str_replace('{elementUid}', $this->I['uid'], $this->I['theItem']);
}
// allStdWrap:
if (is_array($this->I['val']['allStdWrap.'])) {
$this->I['theItem'] = $this->WMcObj->stdWrap($this->I['theItem'], $this->I['val']['allStdWrap.']);
}
$tsfe->imagesOnPage[] = $this->I['val']['output_file'];
$this->extProc_afterLinking($key);
}
}
return $this->extProc_finish();
}
/**
* Called right before the traversing of $this->result begins.
* Can be used for various initialization
*
* @see writeMenu()
*/
protected function extProc_init()
{
}
/**
* Called after all processing for RollOver of an element has been done.
*
* @param int $key Pointer to $this->menuArr[$key] where the current menu element record is found OR $this->result['RO'][$key] where the configuration for that elements RO version is found!
* @see writeMenu()
*/
protected function extProc_RO($key)
{
}
/**
* Called right before the creation of the link for the menu item
*
* @param int $key Pointer to $this->menuArr[$key] where the current menu element record is found
* @see writeMenu()
*/
protected function extProc_beforeLinking($key)
{
}
/**
* Called right after the creation of links for the menu item. This is also the last function call before the
* for-loop traversing menu items goes to the next item.
* This function MUST set $this->WMresult.=[HTML for menu item] to add the generated menu item to the internal accumulation of items.
* Further this calls the subMenu function in the parent class to create any submenu there might be.
*
* @param int $key Pointer to $this->menuArr[$key] where the current menu element record is found
* @see writeMenu(), AbstractMenuContentObject::subMenu()
*/
protected function extProc_afterLinking($key)
{
// Add part to the accumulated result + fetch submenus
if (!$this->I['spacer']) {
$this->I['theItem'] .= $this->subMenu($this->I['uid'], $this->WMsubmenuObjSuffixes[$key]['sOSuffix']);
}
$part = isset($this->I['val']['wrapItemAndSub.']) ? $this->WMcObj->stdWrap($this->I['val']['wrapItemAndSub'], $this->I['val']['wrapItemAndSub.']) : $this->I['val']['wrapItemAndSub'];
$this->WMresult .= $part ? $this->WMcObj->wrap($this->I['theItem'], $part) : $this->I['theItem'];
}
/**
* Called before the "wrap" happens on the menu item.
*
* @param string $item The current content of the menu item, $this->I['theItem'], passed along.
* @param int $key Pointer to $this->menuArr[$key] where the current menu element record is found (unused)
* @return string The modified version of $item, going back into $this->I['theItem']
* @see writeMenu()
*/
protected function extProc_beforeAllWrap($item, $key)
{
return $item;
}
/**
* Called before the writeMenu() function returns (only if a menu was generated)
*
* @return string The total menu content should be returned by this function
* @see writeMenu()
*/
protected function extProc_finish()
{
// stdWrap:
if (is_array($this->mconf['stdWrap.'])) {
$this->WMresult = $this->WMcObj->stdWrap($this->WMresult, $this->mconf['stdWrap.']);
}
return $this->WMcObj->wrap($this->WMresult, $this->mconf['wrap']) . $this->WMextraScript;
}
/**
* This explodes a comma-list into an array where the values are parsed through ContentObjectRender::calc() and cast to (int)(so you are sure to have integers in the output array)
* Used to split and calculate min and max values for GMENUs.
*
* @param string $string The string with parts in (where each part is evaluated by ->calc())
* @return array And array with evaluated values.
* @see ContentObjectRenderer::calc(), makeGifs()
*/
protected function calcIntExplode($string)
{
$temp = explode(',', $string);
foreach ($temp as $key => $val) {
$temp[$key] = (int)$this->parent_cObj->calc($val);
}
return $temp;
}
}
@@ -0,0 +1,252 @@
<?php
namespace TYPO3\CMS\Frontend\ContentObject\Menu;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3\CMS\Core\Core\Environment;
use TYPO3\CMS\Core\Type\File\ImageInfo;
use TYPO3\CMS\Core\Utility\ArrayUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
use TYPO3\CMS\Frontend\Imaging\GifBuilder;
/**
* ImageMap based menus
*
* @deprecated since TYPO3 v9.4, will be removed in TYPO3 v10.0
*/
class ImageMenuContentObject extends AbstractMenuContentObject
{
public function __construct()
{
trigger_error('IMGMENU and ImageMenuContentObject will be removed in TYPO3 v10.0, you should build accessible websites with TMENU/Text, and optional images on top, which can be achieved with TypoScript.', E_USER_DEPRECATED);
}
/**
* Calls procesItemStates() so that the common configuration for the menu items are resolved into individual configuration per item.
* Calls makeImageMap() to generate the image map image-file
*
* @see AbstractMenuContentObject::procesItemStates(), makeImageMap()
*/
public function generate()
{
$NOconf = [];
$splitCount = count($this->menuArr);
if ($splitCount) {
list($NOconf) = $this->procesItemStates($splitCount);
}
if (!empty($this->mconf['debugItemConf'])) {
echo '<h3>$NOconf:</h3>';
debug($NOconf);
}
$this->makeImageMap($NOconf);
}
/**
* Will traverse input array with configuration per-item and create corresponding GIF files for the menu.
* The data of the files are stored in $this->result
*
* @param array $conf Array with configuration for each item.
* @internal
* @see generate()
*/
public function makeImageMap($conf)
{
if (!is_array($conf)) {
$conf = [];
}
if (is_array($this->mconf['main.'])) {
$gifCreator = GeneralUtility::makeInstance(GifBuilder::class);
$itemsConf = $conf;
$conf = $this->mconf['main.'];
if (is_array($conf)) {
$sKeyArray = ArrayUtility::filterAndSortByNumericKeys($conf);
$gifObjCount = (int)end($sKeyArray);
// Now we add graphical objects to the gifbuilder-setup
$waArr = [];
foreach ($itemsConf as $key => $val) {
if (is_array($val)) {
$gifObjCount++;
$waArr[$key]['free'] = $gifObjCount;
$sKeyArray = ArrayUtility::filterAndSortByNumericKeys($val);
foreach ($sKeyArray as $theKey) {
$theValue = $val[$theKey];
if ((int)$theKey && ($theValArr = $val[$theKey . '.'])) {
$cObjData = $this->menuArr[$key] ?: [];
$gifObjCount++;
if ($theValue === 'TEXT') {
$waArr[$key]['textNum'] = $gifObjCount;
$gifCreator->data = $cObjData;
$theValArr = $gifCreator->checkTextObj($theValArr);
// if this is not done it seems that imageMaps will be rendered wrong!!
unset($theValArr['text.']);
// check links
$LD = $this->menuTypoLink($this->menuArr[$key], $this->mconf['target'], '', '', [], '', $this->mconf['forceTypeValue']);
// If access restricted pages should be shown in menus, change the link of such pages to link to a redirection page:
$this->changeLinksForAccessRestrictedPages($LD, $this->menuArr[$key], $this->mconf['target'], $this->mconf['forceTypeValue']);
// Overriding URL / Target if set to do so:
if ($this->menuArr[$key]['_OVERRIDE_HREF']) {
$LD['totalURL'] = $this->menuArr[$key]['_OVERRIDE_HREF'];
if ($this->menuArr[$key]['_OVERRIDE_TARGET']) {
$LD['target'] = $this->menuArr[$key]['_OVERRIDE_TARGET'];
}
}
// Setting target/url for Image Map:
if ($theValArr['imgMap.']['url'] === '') {
$theValArr['imgMap.']['url'] = $LD['totalURL'];
}
if ($theValArr['imgMap.']['target'] === '') {
$theValArr['imgMap.']['target'] = $LD['target'];
}
if (is_array($theValArr['imgMap.']['altText.'])) {
$cObj = GeneralUtility::makeInstance(ContentObjectRenderer::class);
$cObj->start($cObjData, 'pages');
if (isset($theValArr['imgMap.']['altText.'])) {
$theValArr['imgMap.']['altText'] = $cObj->stdWrap($theValArr['imgMap.']['altText'], $theValArr['imgMap.']['altText.']);
}
unset($theValArr['imgMap.']['altText.']);
}
if (is_array($theValArr['imgMap.']['titleText.'])) {
$cObj = GeneralUtility::makeInstance(ContentObjectRenderer::class);
$cObj->start($cObjData, 'pages');
if (isset($theValArr['imgMap.']['titleText.'])) {
$theValArr['imgMap.']['titleText'] = $cObj->stdWrap($theValArr['imgMap.']['titleText'], $theValArr['imgMap.']['titleText.']);
}
unset($theValArr['imgMap.']['titleText.']);
}
}
// This code goes one level in if the object is an image. If 'file' and/or 'mask' appears to be GIFBUILDER-objects, they are both searched for TEXT objects, and if a textobj is found, it's checked with the currently loaded record!!
if ($theValue === 'IMAGE') {
if ($theValArr['file'] === 'GIFBUILDER') {
$temp_sKeyArray = ArrayUtility::filterAndSortByNumericKeys($theValArr['file.']);
foreach ($temp_sKeyArray as $temp_theKey) {
if ($theValArr['mask.'][$temp_theKey] === 'TEXT') {
$gifCreator->data = $this->menuArr[$key] ?: [];
$theValArr['mask.'][$temp_theKey . '.'] = $gifCreator->checkTextObj($theValArr['mask.'][$temp_theKey . '.']);
// If this is not done it seems that imageMaps will be rendered wrong!!
unset($theValArr['mask.'][$temp_theKey . '.']['text.']);
}
}
}
if ($theValArr['mask'] === 'GIFBUILDER') {
$temp_sKeyArray = ArrayUtility::filterAndSortByNumericKeys($theValArr['mask.']);
foreach ($temp_sKeyArray as $temp_theKey) {
if ($theValArr['mask.'][$temp_theKey] === 'TEXT') {
$gifCreator->data = $this->menuArr[$key] ?: [];
$theValArr['mask.'][$temp_theKey . '.'] = $gifCreator->checkTextObj($theValArr['mask.'][$temp_theKey . '.']);
// if this is not done it seems that imageMaps will be rendered wrong!!
unset($theValArr['mask.'][$temp_theKey . '.']['text.']);
}
}
}
}
// Checks if disabled is set...
$setObjFlag = 1;
if ($theValArr['if.']) {
/** @var ContentObjectRenderer $cObj */
$cObj = GeneralUtility::makeInstance(ContentObjectRenderer::class);
$cObj->start($cObjData, 'pages');
if (!empty($theValArr['if.']) && !$cObj->checkIf($theValArr['if.'])) {
$setObjFlag = 0;
}
unset($theValArr['if.']);
}
// Set the object!
if ($setObjFlag) {
$conf[$gifObjCount] = $theValue;
$conf[$gifObjCount . '.'] = $theValArr;
}
}
}
}
}
$gifCreator->start($conf, $this->getTypoScriptFrontendController()->page);
// calculations
$dConf = [];
foreach ($waArr as $key => $val) {
if ($dConf[$key] = $itemsConf[$key]['distrib']) {
$textBB = $gifCreator->objBB[$val['textNum']];
$dConf[$key] = str_replace(
['textX', 'textY'],
[$textBB[0], $textBB[1]],
$dConf[$key]
);
$dConf[$key] = GeneralUtility::intExplode(',', $gifCreator->calcOffset($dConf[$key]));
}
}
$workArea = GeneralUtility::intExplode(',', $gifCreator->calcOffset($this->mconf['dWorkArea']));
foreach ($waArr as $key => $val) {
$index = $val['free'];
$gifCreator->setup[$index] = 'WORKAREA';
$workArea[2] = $dConf[$key][2] ?: $dConf[$key][0];
$workArea[3] = $dConf[$key][3] ?: $dConf[$key][1];
$gifCreator->setup[$index . '.']['set'] = implode(',', $workArea);
$workArea[0] += $dConf[$key][0];
$workArea[1] += $dConf[$key][1];
}
if ($this->mconf['debugRenumberedObject']) {
echo '<h3>Renumbered GIFBUILDER object:</h3>';
debug($gifCreator->setup);
}
GeneralUtility::mkdir_deep(Environment::getPublicPath() . '/typo3temp/assets/menu/');
$gifFileName = $gifCreator->fileName('assets/menu/');
// Gets the ImageMap from the cache...
$cache = $this->getCache();
$imgHash = md5($gifFileName);
$imgMap = $cache->get($imgHash);
// File exists
if ($imgMap && file_exists($gifFileName)) {
$imageInfo = GeneralUtility::makeInstance(ImageInfo::class, $gifFileName);
$w = $imageInfo->getWidth();
$h = $imageInfo->getHeight();
} else {
// file is generated
$gifCreator->make();
$w = $gifCreator->w;
$h = $gifCreator->h;
$gifCreator->output($gifFileName);
$gifCreator->destroy();
$imgMap = $gifCreator->map;
$cache->set($imgHash, $imgMap, ['ident_MENUIMAGEMAP'], 0);
}
$imgMap .= $this->mconf['imgMapExtras'];
$this->result = ['output_file' => $gifFileName, 'output_w' => $w, 'output_h' => $h, 'imgMap' => $imgMap];
}
}
}
/**
* Returns the HTML for the image map menu.
* If ->result is TRUE it will create the HTML for the image map menu.
*
* @return string The HTML for the menu
*/
public function writeMenu()
{
if ($this->result) {
$res = $this->result;
// shortMD5 260900
$menuName = 'menu_' . GeneralUtility::shortMD5($res['imgMap']);
$result = '<img src="' . $this->getTypoScriptFrontendController()->absRefPrefix . $res['output_file'] . '" width="' . $res['output_w'] . '" height="' . $res['output_h'] . '" usemap="#' . $menuName . '" border="0" ' . $this->mconf['params'];
// Adding alt attribute if not set.
if (!strstr($result, 'alt="')) {
$result .= ' alt="Menu Image Map"';
}
$result .= ' /><map name="' . $menuName . '" id="' . $menuName . '">' . $res['imgMap'] . '</map>';
$this->getTypoScriptFrontendController()->imagesOnPage[] = $res['output_file'];
return $this->WMcObj->wrap($result, $this->mconf['wrap']);
}
return '';
}
}
@@ -0,0 +1,77 @@
<?php
namespace TYPO3\CMS\Frontend\ContentObject\Menu;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3\CMS\Core\SingletonInterface;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Factory for menu content objects. Allows overriding the default
* types like 'GMENU' with an own implementation (only one possible)
* and new types can be registered.
* @internal this is only used for internal purposes and solely used for EXT:frontend and not part of TYPO3's Core API.
*/
class MenuContentObjectFactory implements SingletonInterface
{
/**
* Register of TypoScript keys to according render class
*
* @var array
*/
protected $menuTypeToClassMapping = [
'GMENU' => GraphicalMenuContentObject::class,
'TMENU' => TextMenuContentObject::class,
'IMGMENU' => ImageMenuContentObject::class,
];
/**
* Gets a typo script string like 'TMENU' and returns an object of this type
*
* @param string $type
* @return AbstractMenuContentObject Menu object
* @throws Exception\NoSuchMenuTypeException
*/
public function getMenuObjectByType($type = '')
{
$upperCasedClassName = strtoupper($type);
if (array_key_exists($upperCasedClassName, $this->menuTypeToClassMapping)) {
$object = GeneralUtility::makeInstance($this->menuTypeToClassMapping[$upperCasedClassName]);
} else {
throw new Exception\NoSuchMenuTypeException(
'Menu type ' . (string)$type . ' has no implementing class.',
1363278130
);
}
return $object;
}
/**
* Register new menu type or override existing type
*
* @param string $type Menu type to be used in TypoScript
* @param string $className Class rendering the menu
* @throws \InvalidArgumentException
*/
public function registerMenuType($type, $className)
{
if (!is_string($type) || !is_string($className)) {
throw new \InvalidArgumentException(
'type and className must be strings',
1363429303
);
}
$this->menuTypeToClassMapping[strtoupper($type)] = $className;
}
}
@@ -0,0 +1,267 @@
<?php
namespace TYPO3\CMS\Frontend\ContentObject\Menu;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use TYPO3\CMS\Core\TypoScript\TypoScriptService;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Extension class creating text based menus
*/
class TextMenuContentObject extends AbstractMenuContentObject
{
/**
* Calls procesItemStates() so that the common configuration for the menu items are resolved into individual configuration per item.
* Sets the result for the new "normal state" in $this->result
*
* @see AbstractMenuContentObject::procesItemStates()
*/
public function generate()
{
$NOconf = [];
$splitCount = count($this->menuArr);
if ($splitCount) {
list($NOconf) = $this->procesItemStates($splitCount);
}
if (!empty($this->mconf['debugItemConf'])) {
echo '<h3>$NOconf:</h3>';
debug($NOconf);
}
$this->result = $NOconf;
}
/**
* Traverses the ->result array of menu items configuration (made by ->generate()) and renders each item.
* During the execution of this function many internal methods prefixed "extProc_" from this class is called and
* many of these are for now dummy functions.
* An instance of ContentObjectRenderer is also made and for each menu item rendered it is loaded with
* the record for that page so that any stdWrap properties that applies will have the current menu items record available.
*
* @return string The HTML for the menu (returns result through $this->extProc_finish(); )
*/
public function writeMenu()
{
if (empty($this->result)) {
return '';
}
$this->WMresult = '';
$this->INPfixMD5 = substr(md5(microtime() . 'tmenu'), 0, 4);
$this->WMmenuItems = count($this->result);
$typoScriptService = GeneralUtility::makeInstance(TypoScriptService::class);
$this->WMsubmenuObjSuffixes = $typoScriptService->explodeConfigurationForOptionSplit(['sOSuffix' => $this->mconf['submenuObjSuffixes']], $this->WMmenuItems);
$this->extProc_init();
foreach ($this->result as $key => $val) {
$GLOBALS['TSFE']->register['count_HMENU_MENUOBJ']++;
$GLOBALS['TSFE']->register['count_MENUOBJ']++;
// Initialize the cObj with the page record of the menu item
$this->WMcObj->start($this->menuArr[$key], 'pages');
$this->I = [];
$this->I['key'] = $key;
$this->I['INPfix'] = ($this->imgNameNotRandom ? '' : '_' . $this->INPfixMD5) . '_' . $key;
$this->I['val'] = $val;
$this->I['title'] = isset($this->I['val']['stdWrap.']) ? $this->WMcObj->stdWrap($this->getPageTitle($this->menuArr[$key]['title'], $this->menuArr[$key]['nav_title']), $this->I['val']['stdWrap.']) : $this->getPageTitle($this->menuArr[$key]['title'], $this->menuArr[$key]['nav_title']);
$this->I['uid'] = $this->menuArr[$key]['uid'];
$this->I['mount_pid'] = $this->menuArr[$key]['mount_pid'];
$this->I['pid'] = $this->menuArr[$key]['pid'];
$this->I['spacer'] = $this->menuArr[$key]['isSpacer'];
// Set access key
if ($this->mconf['accessKey']) {
$this->I['accessKey'] = $this->accessKey($this->I['title']);
} else {
$this->I['accessKey'] = [];
}
// Make link tag
$this->I['val']['ATagParams'] = $this->WMcObj->getATagParams($this->I['val']);
if (isset($this->I['val']['additionalParams.'])) {
$this->I['val']['additionalParams'] = $this->WMcObj->stdWrap($this->I['val']['additionalParams'], $this->I['val']['additionalParams.']);
}
$this->I['linkHREF'] = $this->link($key, $this->I['val']['altTarget'], $this->mconf['forceTypeValue']);
if (empty($this->I['linkHREF'])) {
$this->I['val']['doNotLinkIt'] = 1;
}
// Title attribute of links:
$titleAttrValue = isset($this->I['val']['ATagTitle.']) ? $this->WMcObj->stdWrap($this->I['val']['ATagTitle'], $this->I['val']['ATagTitle.']) . $this->I['accessKey']['alt'] : $this->I['val']['ATagTitle'] . $this->I['accessKey']['alt'];
if ($titleAttrValue !== '') {
$this->I['linkHREF']['title'] = $titleAttrValue;
}
// Calling extra processing function
$this->extProc_beforeLinking($key);
// stdWrap for doNotLinkIt
if (isset($this->I['val']['doNotLinkIt.'])) {
$this->I['val']['doNotLinkIt'] = $this->WMcObj->stdWrap($this->I['val']['doNotLinkIt'], $this->I['val']['doNotLinkIt.']);
}
// Compile link tag
if (!$this->I['val']['doNotLinkIt']) {
$this->I['val']['doNotLinkIt'] = 0;
}
if (!$this->I['spacer'] && $this->I['val']['doNotLinkIt'] != 1) {
$this->setATagParts();
} else {
$this->I['A1'] = '';
$this->I['A2'] = '';
}
// ATagBeforeWrap processing:
if ($this->I['val']['ATagBeforeWrap']) {
$wrapPartsBefore = explode('|', $this->I['val']['linkWrap']);
$wrapPartsAfter = ['', ''];
} else {
$wrapPartsBefore = ['', ''];
$wrapPartsAfter = explode('|', $this->I['val']['linkWrap']);
}
if ($this->I['val']['stdWrap2'] || isset($this->I['val']['stdWrap2.'])) {
$stdWrap2 = isset($this->I['val']['stdWrap2.']) ? $this->WMcObj->stdWrap('|', $this->I['val']['stdWrap2.']) : '|';
$wrapPartsStdWrap = explode($this->I['val']['stdWrap2'] ? $this->I['val']['stdWrap2'] : '|', $stdWrap2);
} else {
$wrapPartsStdWrap = ['', ''];
}
// Make before, middle and after parts
$this->I['parts'] = [];
$this->I['parts']['before'] = $this->getBeforeAfter('before');
$this->I['parts']['stdWrap2_begin'] = $wrapPartsStdWrap[0];
// stdWrap for doNotShowLink
if (isset($this->I['val']['doNotShowLink.'])) {
$this->I['val']['doNotShowLink'] = $this->WMcObj->stdWrap($this->I['val']['doNotShowLink'], $this->I['val']['doNotShowLink.']);
}
if (!$this->I['val']['doNotShowLink']) {
$this->I['parts']['notATagBeforeWrap_begin'] = $wrapPartsAfter[0];
$this->I['parts']['ATag_begin'] = $this->I['A1'];
$this->I['parts']['ATagBeforeWrap_begin'] = $wrapPartsBefore[0];
$this->I['parts']['title'] = $this->I['title'];
$this->I['parts']['ATagBeforeWrap_end'] = $wrapPartsBefore[1];
$this->I['parts']['ATag_end'] = $this->I['A2'];
$this->I['parts']['notATagBeforeWrap_end'] = $wrapPartsAfter[1];
}
$this->I['parts']['stdWrap2_end'] = $wrapPartsStdWrap[1];
$this->I['parts']['after'] = $this->getBeforeAfter('after');
// Passing I to a user function
if ($this->mconf['IProcFunc']) {
$this->I = $this->userProcess('IProcFunc', $this->I);
}
// Merge parts + beforeAllWrap
$this->I['theItem'] = implode('', $this->I['parts']);
$this->I['theItem'] = $this->extProc_beforeAllWrap($this->I['theItem'], $key);
// allWrap:
$allWrap = isset($this->I['val']['allWrap.']) ? $this->WMcObj->stdWrap($this->I['val']['allWrap'], $this->I['val']['allWrap.']) : $this->I['val']['allWrap'];
$this->I['theItem'] = $this->WMcObj->wrap($this->I['theItem'], $allWrap);
if ($this->I['val']['subst_elementUid']) {
$this->I['theItem'] = str_replace('{elementUid}', $this->I['uid'], $this->I['theItem']);
}
// allStdWrap:
if (is_array($this->I['val']['allStdWrap.'])) {
$this->I['theItem'] = $this->WMcObj->stdWrap($this->I['theItem'], $this->I['val']['allStdWrap.']);
}
// Calling extra processing function
$this->extProc_afterLinking($key);
}
return $this->extProc_finish();
}
/**
* Generates the before* and after* images for TMENUs
*
* @param string $pref Can be "before" or "after" and determines which kind of image to create (basically this is the prefix of the TypoScript properties that are read from the ->I['val'] array
* @return string The resulting HTML of the image, if any.
*/
protected function getBeforeAfter($pref)
{
$res = '';
if ($imgInfo = $this->WMcObj->getImgResource($this->I['val'][$pref . 'Img'], $this->I['val'][$pref . 'Img.'])) {
$theName = $this->imgNamePrefix . $this->I['uid'] . $this->I['INPfix'] . $pref;
$name = ' ' . $this->nameAttribute . '="' . $theName . '"';
$GLOBALS['TSFE']->imagesOnPage[] = $imgInfo[3];
$res = '<img' . ' src="' . $GLOBALS['TSFE']->absRefPrefix . $imgInfo[3] . '"' . ' width="' . $imgInfo[0] . '"' . ' height="' . $imgInfo[1] . '"' . $name . ($this->I['val'][$pref . 'ImgTagParams'] ? ' ' . $this->I['val'][$pref . 'ImgTagParams'] : '') . $this->parent_cObj->getBorderAttr(' border="0"');
if (!strstr($res, 'alt="')) {
// Adding alt attribute if not set.
$res .= ' alt=""';
}
$res .= ' />';
if ($this->I['val'][$pref . 'ImgLink']) {
$res = $this->I['A1'] . $res . $this->I['A2'];
}
}
$processedPref = isset($this->I['val'][$pref . '.']) ? $this->WMcObj->stdWrap($this->I['val'][$pref], $this->I['val'][$pref . '.']) : $this->I['val'][$pref];
if (isset($this->I['val'][$pref . 'Wrap'])) {
return $this->WMcObj->wrap($res . $processedPref, $this->I['val'][$pref . 'Wrap']);
}
return $res . $processedPref;
}
/**
* Called right before the traversing of $this->result begins.
* Can be used for various initialization
*
* @see writeMenu()
*/
protected function extProc_init()
{
}
/**
* Called right before the creation of the link for the menu item
*
* @param int $key Pointer to $this->menuArr[$key] where the current menu element record is found
* @see writeMenu()
*/
protected function extProc_beforeLinking($key)
{
}
/**
* Called right after the creation of links for the menu item. This is also the last function call before the while-loop traversing menu items goes to the next item.
* This function MUST set $this->WMresult.=[HTML for menu item] to add the generated menu item to the internal accumulation of items.
*
* @param int $key Pointer to $this->menuArr[$key] where the current menu element record is found
* @see writeMenu()
*/
protected function extProc_afterLinking($key)
{
// Add part to the accumulated result + fetch submenus
if (!$this->I['spacer']) {
$this->I['theItem'] .= $this->subMenu($this->I['uid'], $this->WMsubmenuObjSuffixes[$key]['sOSuffix']);
}
$part = isset($this->I['val']['wrapItemAndSub.']) ? $this->WMcObj->stdWrap($this->I['val']['wrapItemAndSub'], $this->I['val']['wrapItemAndSub.']) : $this->I['val']['wrapItemAndSub'];
$this->WMresult .= $part ? $this->WMcObj->wrap($this->I['theItem'], $part) : $this->I['theItem'];
}
/**
* Called before the "allWrap" happens on the menu item.
*
* @param string $item The current content of the menu item, $this->I['theItem'], passed along.
* @param int $key Pointer to $this->menuArr[$key] where the current menu element record is found
* @return string The modified version of $item, going back into $this->I['theItem']
* @see writeMenu()
*/
protected function extProc_beforeAllWrap($item, $key)
{
return $item;
}
/**
* Called before the writeMenu() function returns (only if a menu was generated)
*
* @return string The total menu content should be returned by this function
* @see writeMenu()
*/
protected function extProc_finish()
{
// stdWrap:
if (is_array($this->mconf['stdWrap.'])) {
$this->WMresult = $this->WMcObj->stdWrap($this->WMresult, $this->mconf['stdWrap.']);
}
return $this->WMcObj->wrap($this->WMresult, $this->mconf['wrap']) . $this->WMextraScript;
}
}