[TASK] Call explicit render() on icon objects
[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\Utility\BackendUtility;
18 use TYPO3\CMS\Core\Imaging\Icon;
19 use TYPO3\CMS\Core\Imaging\IconFactory;
20 use TYPO3\CMS\Core\Utility\GeneralUtility;
21 use TYPO3\CMS\Backend\ContextMenu\ContextMenuAction;
22 use TYPO3\CMS\Backend\ContextMenu\ContextMenuActionCollection;
23 use TYPO3\CMS\Backend\Tree\TreeNode;
24
25
26 /**
27 * Abstract Context Menu Data Provider
28 */
29 abstract class AbstractContextMenuDataProvider {
30
31 /**
32 * List of actions that are generally disabled
33 *
34 * @var array
35 */
36 protected $disableItems = array();
37
38 /**
39 * Context Menu Type (e.g. table.pages, table.tt_content)
40 *
41 * @var string
42 */
43 protected $contextMenuType = '';
44
45 /**
46 * Returns the context menu type
47 *
48 * @return string
49 */
50 public function getContextMenuType() {
51 return $this->contextMenuType;
52 }
53
54 /**
55 * Sets the context menu type
56 *
57 * @param string $contextMenuType
58 * @return void
59 */
60 public function setContextMenuType($contextMenuType) {
61 $this->contextMenuType = $contextMenuType;
62 }
63
64 /**
65 * Returns the actions of the node
66 *
67 * @param TreeNode $node
68 * @return ContextMenuActionCollection
69 */
70 abstract public function getActionsForNode(TreeNode $node);
71
72 /**
73 * Returns the configuration of the specified context menu type
74 *
75 * @return array
76 */
77 protected function getConfiguration() {
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 if ($displayCondition === '') {
96 return TRUE;
97 }
98 // Parse condition string
99 $conditions = array();
100 preg_match_all('/(.+?)(>=|<=|!=|=|>|<)(.+?)(\\|\\||&&|$)/is', $displayCondition, $conditions);
101 $lastResult = FALSE;
102 $chainType = '';
103 $amountOfConditions = count($conditions[0]);
104 for ($i = 0; $i < $amountOfConditions; ++$i) {
105 // Check method for existence
106 $method = trim($conditions[1][$i]);
107 list($method, $index) = explode('|', $method);
108 if (!method_exists($node, $method)) {
109 continue;
110 }
111 // Fetch compare value
112 $returnValue = call_user_func(array($node, $method));
113 if (is_array($returnValue)) {
114 $returnValue = $returnValue[$index];
115 }
116 // Compare fetched and expected values
117 $operator = trim($conditions[2][$i]);
118 $expected = trim($conditions[3][$i]);
119 if ($operator === '=') {
120 $returnValue = $returnValue == $expected;
121 } elseif ($operator === '>') {
122 $returnValue = $returnValue > $expected;
123 } elseif ($operator === '<') {
124 $returnValue = $returnValue < $expected;
125 } elseif ($operator === '>=') {
126 $returnValue = $returnValue >= $expected;
127 } elseif ($operator === '<=') {
128 $returnValue = $returnValue <= $expected;
129 } elseif ($operator === '!=') {
130 $returnValue = $returnValue != $expected;
131 } else {
132 $returnValue = FALSE;
133 $lastResult = FALSE;
134 }
135 // Chain last result and the current if requested
136 if ($chainType === '||') {
137 $lastResult = $lastResult || $returnValue;
138 } elseif ($chainType === '&&') {
139 $lastResult = $lastResult && $returnValue;
140 } else {
141 $lastResult = $returnValue;
142 }
143 // Save chain type for the next condition
144 $chainType = trim($conditions[4][$i]);
145 }
146 return $lastResult;
147 }
148
149 /**
150 * Returns the next context menu level
151 *
152 * @param array $actions
153 * @param TreeNode $node
154 * @param int $level
155 * @return ContextMenuActionCollection
156 */
157 protected function getNextContextMenuLevel(array $actions, TreeNode $node, $level = 0) {
158 /** @var $actionCollection ContextMenuActionCollection */
159 $actionCollection = GeneralUtility::makeInstance(ContextMenuActionCollection::class);
160 /** @var $iconFactory IconFactory */
161 $iconFactory = GeneralUtility::makeInstance(IconFactory::class);
162 if ($level > 5) {
163 return $actionCollection;
164 }
165 $type = '';
166 foreach ($actions as $index => $actionConfiguration) {
167 if (substr($index, -1) !== '.') {
168 $type = $actionConfiguration;
169 if ($type !== 'DIVIDER') {
170 continue;
171 }
172 }
173 if (!in_array($type, array('DIVIDER', 'SUBMENU', 'ITEM'))) {
174 continue;
175 }
176 /** @var $action ContextMenuAction */
177 $action = GeneralUtility::makeInstance(ContextMenuAction::class);
178 $action->setId($index);
179 if ($type === 'DIVIDER') {
180 $action->setType('divider');
181 } else {
182 if (in_array($actionConfiguration['name'], $this->disableItems) || isset($actionConfiguration['displayCondition']) && trim($actionConfiguration['displayCondition']) !== '' && !$this->evaluateDisplayCondition($node, $actionConfiguration['displayCondition'])) {
183 unset($action);
184 continue;
185 }
186 $label = $this->getLanguageService()->sL($actionConfiguration['label'], TRUE);
187 if ($type === 'SUBMENU') {
188 $action->setType('submenu');
189 $action->setChildActions($this->getNextContextMenuLevel($actionConfiguration, $node, $level + 1));
190 } else {
191 $action->setType('action');
192 $action->setCallbackAction($actionConfiguration['callbackAction']);
193 if (is_array($actionConfiguration['customAttributes.'])) {
194 if (!empty($actionConfiguration['customAttributes.']['contentUrl'])) {
195 $actionConfiguration['customAttributes.']['contentUrl'] = $this->replaceModuleTokenInContentUrl($actionConfiguration['customAttributes.']['contentUrl']);
196 }
197 $action->setCustomAttributes($actionConfiguration['customAttributes.']);
198 }
199 }
200 $action->setLabel($label);
201 if (!isset($actionConfiguration['iconName'])) {
202 $actionConfiguration['iconName'] = 'miscellaneous-placeholder';
203 }
204 $action->setIcon($iconFactory->getIcon($actionConfiguration['iconName'], Icon::SIZE_SMALL)->render());
205 }
206 $actionCollection->offsetSet($level . (int)$index, $action);
207 $actionCollection->ksort();
208 }
209 return $actionCollection;
210 }
211
212 /**
213 * Add the CSRF token to the module URL if a "M" parameter is found
214 *
215 * @param string $contentUrl
216 * @return string
217 */
218 protected function replaceModuleTokenInContentUrl($contentUrl) {
219 $parsedUrl = parse_url($contentUrl);
220 parse_str($parsedUrl['query'], $urlParameters);
221 if (isset($urlParameters['M'])) {
222 $moduleName = $urlParameters['M'];
223 unset($urlParameters['M']);
224 $contentUrl = BackendUtility::getModuleUrl($moduleName, $urlParameters);
225 }
226 return $contentUrl;
227 }
228
229 /**
230 * Returns LanguageService
231 *
232 * @return \TYPO3\CMS\Lang\LanguageService
233 */
234 protected function getLanguageService() {
235 return $GLOBALS['LANG'];
236 }
237
238 /**
239 * Returns the current BE user.
240 *
241 * @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
242 */
243 protected function getBackendUser() {
244 return $GLOBALS['BE_USER'];
245 }
246
247 }