Commit fefdb139 authored by Frank Nägler's avatar Frank Nägler Committed by Wouter Wolters
Browse files

[!!!][TASK] Cleanup ContextMenu classes

As a pre-patch for the cleanups in the page tree, this patch removes unused
classes and simplifies the class structure. All this classes never used
outside the core.
A search in the TER doesn't find any usages.
For this reason, we decided to not deprecate the classes but made a
breaking change.

Resolves: #76527
Releases: master
Change-Id: I5a180999d5fd72dca8467e986e5870071091533c
Reviewed-on: https://review.typo3.org/48511


Reviewed-by: Christian Kuhn's avatarChristian Kuhn <lolli@schwarzbu.ch>
Tested-by: Christian Kuhn's avatarChristian Kuhn <lolli@schwarzbu.ch>
Reviewed-by: Wouter Wolters's avatarWouter Wolters <typo3@wouterwolters.nl>
Tested-by: Wouter Wolters's avatarWouter Wolters <typo3@wouterwolters.nl>
parent 0187a797
<?php
namespace TYPO3\CMS\Backend\ContextMenu;
/*
* 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!
*/
/**
* Abstract Context Menu
*/
abstract class AbstractContextMenu
{
/**
* Data Provider
*
* @var \TYPO3\CMS\Backend\ContextMenu\AbstractContextMenuDataProvider
*/
protected $dataProvider = null;
/**
* @param \TYPO3\CMS\Backend\ContextMenu\AbstractContextMenuDataProvider $dataProvider
* @return void
*/
public function setDataProvider(\TYPO3\CMS\Backend\ContextMenu\AbstractContextMenuDataProvider $dataProvider)
{
$this->dataProvider = $dataProvider;
}
/**
* @return \TYPO3\CMS\Backend\ContextMenu\AbstractContextMenuDataProvider
*/
public function getDataProvider()
{
return $this->dataProvider;
}
/**
* Returns the actions for the given node information
*
* @param \TYPO3\CMS\Backend\Tree\TreeNode $node
* @return array
*/
abstract public function getActionsForNode(\TYPO3\CMS\Backend\Tree\TreeNode $node);
}
<?php
namespace TYPO3\CMS\Backend\ContextMenu;
/*
* 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\Backend\Tree\TreeNode;
use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Core\Imaging\Icon;
use TYPO3\CMS\Core\Imaging\IconFactory;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Abstract Context Menu Data Provider
*/
abstract class AbstractContextMenuDataProvider
{
/**
* List of actions that are generally disabled
*
* @var array
*/
protected $disableItems = array();
/**
* Context Menu Type (e.g. table.pages, table.tt_content)
*
* @var string
*/
protected $contextMenuType = '';
/**
* Returns the context menu type
*
* @return string
*/
public function getContextMenuType()
{
return $this->contextMenuType;
}
/**
* Sets the context menu type
*
* @param string $contextMenuType
* @return void
*/
public function setContextMenuType($contextMenuType)
{
$this->contextMenuType = $contextMenuType;
}
/**
* Returns the actions of the node
*
* @param TreeNode $node
* @return ContextMenuActionCollection
*/
abstract public function getActionsForNode(TreeNode $node);
/**
* Returns the configuration of the specified context menu type
*
* @return array
*/
protected function getConfiguration()
{
$contextMenuActions = $this->getBackendUser()->getTSConfig('options.contextMenu.' . $this->contextMenuType . '.items');
return $contextMenuActions['properties'];
}
/**
* Evaluates a given display condition and returns TRUE if the condition matches
*
* Examples:
* getContextInfo|inCutMode:1 || isInCopyMode:1
* isLeafNode:1
* isLeafNode:1 && isInCutMode:1
*
* @param TreeNode $node
* @param string $displayCondition
* @return bool
*/
protected function evaluateDisplayCondition(TreeNode $node, $displayCondition)
{
if ($displayCondition === '') {
return true;
}
// Parse condition string
$conditions = array();
preg_match_all('/(.+?)(>=|<=|!=|=|>|<)(.+?)(\\|\\||&&|$)/is', $displayCondition, $conditions);
$lastResult = false;
$chainType = '';
$amountOfConditions = count($conditions[0]);
for ($i = 0; $i < $amountOfConditions; ++$i) {
// Check method for existence
$method = trim($conditions[1][$i]);
list($method, $index) = explode('|', $method);
if (!method_exists($node, $method)) {
continue;
}
// Fetch compare value
$returnValue = call_user_func(array($node, $method));
if (is_array($returnValue)) {
$returnValue = $returnValue[$index];
}
// Compare fetched and expected values
$operator = trim($conditions[2][$i]);
$expected = trim($conditions[3][$i]);
if ($operator === '=') {
$returnValue = $returnValue == $expected;
} elseif ($operator === '>') {
$returnValue = $returnValue > $expected;
} elseif ($operator === '<') {
$returnValue = $returnValue < $expected;
} elseif ($operator === '>=') {
$returnValue = $returnValue >= $expected;
} elseif ($operator === '<=') {
$returnValue = $returnValue <= $expected;
} elseif ($operator === '!=') {
$returnValue = $returnValue != $expected;
} else {
$returnValue = false;
$lastResult = false;
}
// Chain last result and the current if requested
if ($chainType === '||') {
$lastResult = $lastResult || $returnValue;
} elseif ($chainType === '&&') {
$lastResult = $lastResult && $returnValue;
} else {
$lastResult = $returnValue;
}
// Save chain type for the next condition
$chainType = trim($conditions[4][$i]);
}
return $lastResult;
}
/**
* Returns the next context menu level
*
* @param array $actions
* @param TreeNode $node
* @param int $level
* @return ContextMenuActionCollection
*/
protected function getNextContextMenuLevel(array $actions, TreeNode $node, $level = 0)
{
/** @var $actionCollection ContextMenuActionCollection */
$actionCollection = GeneralUtility::makeInstance(ContextMenuActionCollection::class);
/** @var $iconFactory IconFactory */
$iconFactory = GeneralUtility::makeInstance(IconFactory::class);
if ($level > 5) {
return $actionCollection;
}
$type = '';
foreach ($actions as $index => $actionConfiguration) {
if (substr($index, -1) !== '.') {
$type = $actionConfiguration;
if ($type !== 'DIVIDER') {
continue;
}
}
if (!in_array($type, array('DIVIDER', 'SUBMENU', 'ITEM'))) {
continue;
}
/** @var $action ContextMenuAction */
$action = GeneralUtility::makeInstance(ContextMenuAction::class);
$action->setId($index);
if ($type === 'DIVIDER') {
$action->setType('divider');
} else {
if (in_array($actionConfiguration['name'], $this->disableItems) || isset($actionConfiguration['displayCondition']) && trim($actionConfiguration['displayCondition']) !== '' && !$this->evaluateDisplayCondition($node, $actionConfiguration['displayCondition'])) {
unset($action);
continue;
}
$label = htmlspecialchars($this->getLanguageService()->sL($actionConfiguration['label']));
if ($type === 'SUBMENU') {
$action->setType('submenu');
$action->setChildActions($this->getNextContextMenuLevel($actionConfiguration, $node, $level + 1));
} else {
$action->setType('action');
$action->setCallbackAction($actionConfiguration['callbackAction']);
if (is_array($actionConfiguration['customAttributes.'])) {
if (!empty($actionConfiguration['customAttributes.']['contentUrl'])) {
$actionConfiguration['customAttributes.']['contentUrl'] = $this->replaceModuleTokenInContentUrl($actionConfiguration['customAttributes.']['contentUrl']);
}
$action->setCustomAttributes($actionConfiguration['customAttributes.']);
}
}
$action->setLabel($label);
if (!isset($actionConfiguration['iconName'])) {
$actionConfiguration['iconName'] = 'miscellaneous-placeholder';
}
$action->setIcon($iconFactory->getIcon($actionConfiguration['iconName'], Icon::SIZE_SMALL)->render());
}
$actionCollection->offsetSet($level . (int)$index, $action);
$actionCollection->ksort();
}
return $actionCollection;
}
/**
* Add the CSRF token to the module URL if a "M" parameter is found
*
* @param string $contentUrl
* @return string
*/
protected function replaceModuleTokenInContentUrl($contentUrl)
{
$parsedUrl = parse_url($contentUrl);
parse_str($parsedUrl['query'], $urlParameters);
if (isset($urlParameters['M'])) {
$moduleName = $urlParameters['M'];
unset($urlParameters['M']);
$contentUrl = BackendUtility::getModuleUrl($moduleName, $urlParameters);
}
return $contentUrl;
}
/**
* Returns LanguageService
*
* @return \TYPO3\CMS\Lang\LanguageService
*/
protected function getLanguageService()
{
return $GLOBALS['LANG'];
}
/**
* Returns the current BE user.
*
* @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
*/
protected function getBackendUser()
{
return $GLOBALS['BE_USER'];
}
}
......@@ -57,9 +57,9 @@ class ContextMenuAction
/**
* Child Action Collection
*
* @var \TYPO3\CMS\Backend\ContextMenu\ContextMenuActionCollection
* @var ContextMenuActionCollection
*/
protected $childActions = null;
protected $childActions;
/**
* Custom Action Attributes
......@@ -173,7 +173,7 @@ class ContextMenuAction
/**
* Returns the child actions
*
* @return \TYPO3\CMS\Backend\ContextMenu\ContextMenuActionCollection
* @return ContextMenuActionCollection
*/
public function getChildActions()
{
......@@ -183,10 +183,10 @@ class ContextMenuAction
/**
* Sets the child actions
*
* @param \TYPO3\CMS\Backend\ContextMenu\ContextMenuActionCollection $actions
* @param ContextMenuActionCollection $actions
* @return void
*/
public function setChildActions(\TYPO3\CMS\Backend\ContextMenu\ContextMenuActionCollection $actions)
public function setChildActions(ContextMenuActionCollection $actions)
{
$this->childActions = $actions;
}
......@@ -198,10 +198,7 @@ class ContextMenuAction
*/
public function hasChildActions()
{
if ($this->childActions !== null) {
return true;
}
return false;
return $this->childActions !== null;
}
/**
......
<?php
namespace TYPO3\CMS\Backend\ContextMenu\Extdirect;
/*
* 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!
*/
/**
* Abstract Context Menu for ExtDirect
*
* This is a concrete implementation that should stay here to be shared
* between the different ExtDirect implementation. Just create a subclass
* for adding specific purposes.
*/
abstract class AbstractExtdirectContextMenu extends \TYPO3\CMS\Backend\ContextMenu\AbstractContextMenu
{
/**
* Returns the actions for the given node information
*
* Note: This method should be overriden to fit your specific needs.
*
* The informations should contain the basic informations of a
* \TYPO3\CMS\Backend\Tree\TreeNode for further processing. Also the classname
* (property type) of the node should be given, because we need this information
* to create the node.
*
* @param \stdClass $nodeData
* @return array
*/
public function getActionsForNodeArray($nodeData)
{
if ($this->dataProvider === null) {
$dataProvider = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Backend\ContextMenu\AbstractContextMenuDataProvider::class);
$this->setDataProvider($dataProvider);
}
/** @var $node \TYPO3\CMS\Backend\Tree\TreeNode */
$node = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Tree\TreeNode::class, (array)$nodeData);
$actions = $this->dataProvider->getActionsForNode($node);
return $actions;
}
/**
* Unused for this implementation
*
* @see getActionsForNodeArray()
* @param \TYPO3\CMS\Backend\Tree\TreeNode $node
* @return array
*/
public function getActionsForNode(\TYPO3\CMS\Backend\Tree\TreeNode $node)
{
}
}
......@@ -14,13 +14,33 @@ namespace TYPO3\CMS\Backend\ContextMenu\Pagetree;
* The TYPO3 project - inspiring people to share!
*/
use TYPO3\CMS\Backend\ContextMenu\ContextMenuAction;
use TYPO3\CMS\Backend\ContextMenu\ContextMenuActionCollection;
use TYPO3\CMS\Backend\Tree\TreeNode;
use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Core\Imaging\Icon;
use TYPO3\CMS\Core\Imaging\IconFactory;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Context Menu Data Provider for the Page Tree
*/
class ContextMenuDataProvider extends \TYPO3\CMS\Backend\ContextMenu\AbstractContextMenuDataProvider
class ContextMenuDataProvider
{
/**
* List of actions that are generally disabled
*
* @var array
*/
protected $disableItems = array();
/**
* Context Menu Type (e.g. table.pages, table.tt_content)
*
* @var string
*/
protected $contextMenuType = '';
/**
* Old Context Menu Options (access mapping)
*
......@@ -28,11 +48,201 @@ class ContextMenuDataProvider extends \TYPO3\CMS\Backend\ContextMenu\AbstractCon
*
* @var array
*/
protected $legacyContextMenuMapping = array(
protected $legacyContextMenuMapping = [
'hide' => 'disable',
'paste' => 'pasteInto,pasteAfter',
'mount_as_treeroot' => 'mountAsTreeroot'
);
];
/**
* Returns the context menu type
*
* @return string
*/
public function getContextMenuType()
{
return $this->contextMenuType;
}
/**
* Sets the context menu type
*
* @param string $contextMenuType
* @return void
*/
public function setContextMenuType($contextMenuType)
{
$this->contextMenuType = $contextMenuType;
}
/**
* Returns the configuration of the specified context menu type
*
* @return array
*/
protected function getConfiguration()
{
$contextMenuActions = $this->getBackendUser()
->getTSConfig('options.contextMenu.' . $this->contextMenuType . '.items');
return $contextMenuActions['properties'];
}
/**
* Evaluates a given display condition and returns TRUE if the condition matches
*
* Examples:
* getContextInfo|inCutMode:1 || isInCopyMode:1
* isLeafNode:1
* isLeafNode:1 && isInCutMode:1
*
* @param TreeNode $node
* @param string $displayCondition
* @return bool
*/
protected function evaluateDisplayCondition(TreeNode $node, $displayCondition)
{
if ($displayCondition === '') {
return true;
}
// Parse condition string
$conditions = array();
preg_match_all('/(.+?)(>=|<=|!=|=|>|<)(.+?)(\\|\\||&&|$)/is', $displayCondition, $conditions);
$lastResult = false;
$chainType = '';
$amountOfConditions = count($conditions[0]);
for ($i = 0; $i < $amountOfConditions; ++$i) {
// Check method for existence
$method = trim($conditions[1][$i]);
list($method, $index) = explode('|', $method);
if (!method_exists($node, $method)) {
continue;
}
// Fetch compare value
$returnValue = call_user_func(array($node, $method));
if (is_array($returnValue)) {
$returnValue = $returnValue[$index];
}
// Compare fetched and expected values
$operator = trim($conditions[2][$i]);
$expected = trim($conditions[3][$i]);
if ($operator === '=') {
$returnValue = $returnValue == $expected;
} elseif ($operator === '>') {
$returnValue = $returnValue > $expected;
} elseif ($operator === '<') {
$returnValue = $returnValue < $expected;
} elseif ($operator === '>=') {
$returnValue = $returnValue >= $expected;
} elseif ($operator === '<=') {
$returnValue = $returnValue <= $expected;
} elseif ($operator === '!=') {
$returnValue = $returnValue != $expected;
} else {
$returnValue = false;
$lastResult = false;
}
// Chain last result and the current if requested
if ($chainType === '||') {
$lastResult = $lastResult || $returnValue;
} elseif ($chainType === '&&') {
$lastResult = $lastResult && $returnValue;
} else {
$lastResult = $returnValue;
}
// Save chain type for the next condition
$chainType = trim($conditions[4][$i]);
}
return $lastResult;
}
/**
* Returns the next context menu level
*
* @param array $actions
* @param TreeNode $node
* @param int $level
* @return ContextMenuActionCollection
*/
protected function getNextContextMenuLevel(array $actions, TreeNode $node, $level = 0)
{
/** @var $actionCollection ContextMenuActionCollection */
$actionCollection = GeneralUtility::makeInstance(ContextMenuActionCollection::class);
/** @var $iconFactory IconFactory */
$iconFactory = GeneralUtility::makeInstance(IconFactory::class);
if ($level > 5) {
return $actionCollection;
}
$type = '';
foreach ($actions as $index => $actionConfiguration) {
if (substr($index, -1) !== '.') {
$type = $actionConfiguration;
if ($type !== 'DIVIDER') {