[TASK] Add unit test to cover stdWrap_innerWrap
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / ContextMenu / AbstractContextMenuDataProvider.php
1 <?php
2 namespace TYPO3\CMS\Backend\ContextMenu;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Backend\Tree\TreeNode;
18 use TYPO3\CMS\Backend\Utility\BackendUtility;
19 use TYPO3\CMS\Core\Imaging\Icon;
20 use TYPO3\CMS\Core\Imaging\IconFactory;
21 use TYPO3\CMS\Core\Utility\GeneralUtility;
22
23 /**
24 * Abstract Context Menu Data Provider
25 */
26 abstract class AbstractContextMenuDataProvider
27 {
28 /**
29 * List of actions that are generally disabled
30 *
31 * @var array
32 */
33 protected $disableItems = array();
34
35 /**
36 * Context Menu Type (e.g. table.pages, table.tt_content)
37 *
38 * @var string
39 */
40 protected $contextMenuType = '';
41
42 /**
43 * Returns the context menu type
44 *
45 * @return string
46 */
47 public function getContextMenuType()
48 {
49 return $this->contextMenuType;
50 }
51
52 /**
53 * Sets the context menu type
54 *
55 * @param string $contextMenuType
56 * @return void
57 */
58 public function setContextMenuType($contextMenuType)
59 {
60 $this->contextMenuType = $contextMenuType;
61 }
62
63 /**
64 * Returns the actions of the node
65 *
66 * @param TreeNode $node
67 * @return ContextMenuActionCollection
68 */
69 abstract public function getActionsForNode(TreeNode $node);
70
71 /**
72 * Returns the configuration of the specified context menu type
73 *
74 * @return array
75 */
76 protected function getConfiguration()
77 {
78 $contextMenuActions = $this->getBackendUser()->getTSConfig('options.contextMenu.' . $this->contextMenuType . '.items');
79 return $contextMenuActions['properties'];
80 }
81
82 /**
83 * Evaluates a given display condition and returns TRUE if the condition matches
84 *
85 * Examples:
86 * getContextInfo|inCutMode:1 || isInCopyMode:1
87 * isLeafNode:1
88 * isLeafNode:1 && isInCutMode:1
89 *
90 * @param TreeNode $node
91 * @param string $displayCondition
92 * @return bool
93 */
94 protected function evaluateDisplayCondition(TreeNode $node, $displayCondition)
95 {
96 if ($displayCondition === '') {
97 return true;
98 }
99 // Parse condition string
100 $conditions = array();
101 preg_match_all('/(.+?)(>=|<=|!=|=|>|<)(.+?)(\\|\\||&&|$)/is', $displayCondition, $conditions);
102 $lastResult = false;
103 $chainType = '';
104 $amountOfConditions = count($conditions[0]);
105 for ($i = 0; $i < $amountOfConditions; ++$i) {
106 // Check method for existence
107 $method = trim($conditions[1][$i]);
108 list($method, $index) = explode('|', $method);
109 if (!method_exists($node, $method)) {
110 continue;
111 }
112 // Fetch compare value
113 $returnValue = call_user_func(array($node, $method));
114 if (is_array($returnValue)) {
115 $returnValue = $returnValue[$index];
116 }
117 // Compare fetched and expected values
118 $operator = trim($conditions[2][$i]);
119 $expected = trim($conditions[3][$i]);
120 if ($operator === '=') {
121 $returnValue = $returnValue == $expected;
122 } elseif ($operator === '>') {
123 $returnValue = $returnValue > $expected;
124 } elseif ($operator === '<') {
125 $returnValue = $returnValue < $expected;
126 } elseif ($operator === '>=') {
127 $returnValue = $returnValue >= $expected;
128 } elseif ($operator === '<=') {
129 $returnValue = $returnValue <= $expected;
130 } elseif ($operator === '!=') {
131 $returnValue = $returnValue != $expected;
132 } else {
133 $returnValue = false;
134 $lastResult = false;
135 }
136 // Chain last result and the current if requested
137 if ($chainType === '||') {
138 $lastResult = $lastResult || $returnValue;
139 } elseif ($chainType === '&&') {
140 $lastResult = $lastResult && $returnValue;
141 } else {
142 $lastResult = $returnValue;
143 }
144 // Save chain type for the next condition
145 $chainType = trim($conditions[4][$i]);
146 }
147 return $lastResult;
148 }
149
150 /**
151 * Returns the next context menu level
152 *
153 * @param array $actions
154 * @param TreeNode $node
155 * @param int $level
156 * @return ContextMenuActionCollection
157 */
158 protected function getNextContextMenuLevel(array $actions, TreeNode $node, $level = 0)
159 {
160 /** @var $actionCollection ContextMenuActionCollection */
161 $actionCollection = GeneralUtility::makeInstance(ContextMenuActionCollection::class);
162 /** @var $iconFactory IconFactory */
163 $iconFactory = GeneralUtility::makeInstance(IconFactory::class);
164 if ($level > 5) {
165 return $actionCollection;
166 }
167 $type = '';
168 foreach ($actions as $index => $actionConfiguration) {
169 if (substr($index, -1) !== '.') {
170 $type = $actionConfiguration;
171 if ($type !== 'DIVIDER') {
172 continue;
173 }
174 }
175 if (!in_array($type, array('DIVIDER', 'SUBMENU', 'ITEM'))) {
176 continue;
177 }
178 /** @var $action ContextMenuAction */
179 $action = GeneralUtility::makeInstance(ContextMenuAction::class);
180 $action->setId($index);
181 if ($type === 'DIVIDER') {
182 $action->setType('divider');
183 } else {
184 if (in_array($actionConfiguration['name'], $this->disableItems) || isset($actionConfiguration['displayCondition']) && trim($actionConfiguration['displayCondition']) !== '' && !$this->evaluateDisplayCondition($node, $actionConfiguration['displayCondition'])) {
185 unset($action);
186 continue;
187 }
188 $label = htmlspecialchars($this->getLanguageService()->sL($actionConfiguration['label']));
189 if ($type === 'SUBMENU') {
190 $action->setType('submenu');
191 $action->setChildActions($this->getNextContextMenuLevel($actionConfiguration, $node, $level + 1));
192 } else {
193 $action->setType('action');
194 $action->setCallbackAction($actionConfiguration['callbackAction']);
195 if (is_array($actionConfiguration['customAttributes.'])) {
196 if (!empty($actionConfiguration['customAttributes.']['contentUrl'])) {
197 $actionConfiguration['customAttributes.']['contentUrl'] = $this->replaceModuleTokenInContentUrl($actionConfiguration['customAttributes.']['contentUrl']);
198 }
199 $action->setCustomAttributes($actionConfiguration['customAttributes.']);
200 }
201 }
202 $action->setLabel($label);
203 if (!isset($actionConfiguration['iconName'])) {
204 $actionConfiguration['iconName'] = 'miscellaneous-placeholder';
205 }
206 $action->setIcon($iconFactory->getIcon($actionConfiguration['iconName'], Icon::SIZE_SMALL)->render());
207 }
208 $actionCollection->offsetSet($level . (int)$index, $action);
209 $actionCollection->ksort();
210 }
211 return $actionCollection;
212 }
213
214 /**
215 * Add the CSRF token to the module URL if a "M" parameter is found
216 *
217 * @param string $contentUrl
218 * @return string
219 */
220 protected function replaceModuleTokenInContentUrl($contentUrl)
221 {
222 $parsedUrl = parse_url($contentUrl);
223 parse_str($parsedUrl['query'], $urlParameters);
224 if (isset($urlParameters['M'])) {
225 $moduleName = $urlParameters['M'];
226 unset($urlParameters['M']);
227 $contentUrl = BackendUtility::getModuleUrl($moduleName, $urlParameters);
228 }
229 return $contentUrl;
230 }
231
232 /**
233 * Returns LanguageService
234 *
235 * @return \TYPO3\CMS\Lang\LanguageService
236 */
237 protected function getLanguageService()
238 {
239 return $GLOBALS['LANG'];
240 }
241
242 /**
243 * Returns the current BE user.
244 *
245 * @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
246 */
247 protected function getBackendUser()
248 {
249 return $GLOBALS['BE_USER'];
250 }
251 }