[TASK] Remove unneeded parenthesis on array-access
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / Backend / ToolbarItems / ShortcutToolbarItem.php
1 <?php
2 namespace TYPO3\CMS\Backend\Backend\ToolbarItems;
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 Psr\Http\Message\ResponseInterface;
18 use Psr\Http\Message\ServerRequestInterface;
19 use TYPO3\CMS\Backend\Module\ModuleLoader;
20 use TYPO3\CMS\Backend\Toolbar\ToolbarItemInterface;
21 use TYPO3\CMS\Backend\Utility\BackendUtility;
22 use TYPO3\CMS\Core\Database\PreparedStatement;
23 use TYPO3\CMS\Core\Imaging\Icon;
24 use TYPO3\CMS\Core\Imaging\IconFactory;
25 use TYPO3\CMS\Core\Page\PageRenderer;
26 use TYPO3\CMS\Core\Resource\Exception\ResourceDoesNotExistException;
27 use TYPO3\CMS\Core\Resource\ResourceFactory;
28 use TYPO3\CMS\Core\Utility\GeneralUtility;
29 use TYPO3\CMS\Core\Utility\MathUtility;
30 use TYPO3\CMS\Core\Utility\PathUtility;
31
32 /**
33 * Class to render the shortcut menu
34 */
35 class ShortcutToolbarItem implements ToolbarItemInterface
36 {
37 /**
38 * @const integer Number of super global group
39 */
40 const SUPERGLOBAL_GROUP = -100;
41
42 /**
43 * @var string
44 */
45 public $perms_clause;
46
47 /**
48 * @var array
49 */
50 public $fieldArray;
51
52 /**
53 * All available shortcuts
54 *
55 * @var array
56 */
57 protected $shortcuts;
58
59 /**
60 * @var array
61 */
62 protected $shortcutGroups;
63
64 /**
65 * Labels of all groups.
66 * If value is 1, the system will try to find a label in the locallang array.
67 *
68 * @var array
69 */
70 protected $groupLabels;
71
72 /**
73 * @var IconFactory
74 */
75 protected $iconFactory;
76
77 /**
78 * Constructor
79 */
80 public function __construct()
81 {
82 $this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
83 if (TYPO3_REQUESTTYPE & TYPO3_REQUESTTYPE_AJAX) {
84 $this->getLanguageService()->includeLLFile('EXT:lang/locallang_misc.xlf');
85 // Needed to get the correct icons when reloading the menu after saving it
86 $loadModules = GeneralUtility::makeInstance(ModuleLoader::class);
87 $loadModules->load($GLOBALS['TBE_MODULES']);
88 }
89
90 // By default, 5 groups are set
91 $this->shortcutGroups = array(
92 1 => '1',
93 2 => '1',
94 3 => '1',
95 4 => '1',
96 5 => '1'
97 );
98 $this->shortcutGroups = $this->initShortcutGroups();
99 $this->shortcuts = $this->initShortcuts();
100
101 $this->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Backend/Toolbar/ShortcutMenu');
102 }
103
104 /**
105 * Checks whether the user has access to this toolbar item
106 *
107 * @return bool TRUE if user has access, FALSE if not
108 */
109 public function checkAccess()
110 {
111 return (bool)$this->getBackendUser()->getTSConfigVal('options.enableBookmarks');
112 }
113
114 /**
115 * Render shortcut icon
116 *
117 * @return string HTML
118 */
119 public function getItem()
120 {
121 $title = $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:toolbarItems.bookmarks', true);
122 return '<span title="' . $title . '">' . $this->iconFactory->getIcon('apps-toolbar-menu-shortcut', Icon::SIZE_SMALL)->render('inline') . '</span>';
123 }
124
125 /**
126 * Render drop down content
127 *
128 * @return string HTML
129 */
130 public function getDropDown()
131 {
132 $languageService = $this->getLanguageService();
133 $shortcutGroup = $languageService->sL('LLL:EXT:lang/locallang_core.xlf:toolbarItems.bookmarksGroup', true);
134 $shortcutEdit = $languageService->sL('LLL:EXT:lang/locallang_core.xlf:toolbarItems.bookmarksEdit', true);
135 $shortcutDelete = $languageService->sL('LLL:EXT:lang/locallang_core.xlf:toolbarItems.bookmarksDelete', true);
136 $editIcon = '<a href="#" class="dropdown-list-link-edit shortcut-edit" ' . $shortcutEdit . '>'
137 . $this->iconFactory->getIcon('actions-open', Icon::SIZE_SMALL)->render('inline') . '</a>';
138 $deleteIcon = '<a href="#" class="dropdown-list-link-delete shortcut-delete" title="' . $shortcutDelete . '">'
139 . $this->iconFactory->getIcon('actions-delete', Icon::SIZE_SMALL)->render('inline') . '</a>';
140
141 $shortcutMenu[] = '<ul class="dropdown-list">';
142
143 // Render shortcuts with no group (group id = 0) first
144 $noGroupShortcuts = $this->getShortcutsByGroup(0);
145 foreach ($noGroupShortcuts as $shortcut) {
146 $shortcutMenu[] = '
147 <li class="shortcut" data-shortcutid="' . (int)$shortcut['raw']['uid'] . '">
148 <a class="dropdown-list-link dropdown-link-list-add-editdelete" href="#" onclick="' . htmlspecialchars($shortcut['action']) . ' return false;">' .
149 $shortcut['icon'] . ' ' .
150 htmlspecialchars($shortcut['label']) .
151 '</a>
152 ' . $editIcon . $deleteIcon . '
153 </li>';
154 }
155 // Now render groups and the contained shortcuts
156 $groups = $this->getGroupsFromShortcuts();
157 krsort($groups, SORT_NUMERIC);
158 foreach ($groups as $groupId => $groupLabel) {
159 if ($groupId != 0) {
160 $shortcutGroup = '';
161 if (count($shortcutMenu) > 1) {
162 $shortcutGroup .= '<li class="divider"></li>';
163 }
164 $shortcutGroup .= '
165 <li class="dropdown-header" id="shortcut-group-' . (int)$groupId . '">
166 ' . $groupLabel . '
167 </li>';
168 $shortcuts = $this->getShortcutsByGroup($groupId);
169 $i = 0;
170 foreach ($shortcuts as $shortcut) {
171 $i++;
172 $shortcutGroup .= '
173 <li class="shortcut" data-shortcutid="' . (int)$shortcut['raw']['uid'] . '" data-shortcutgroup="' . (int)$groupId . '">
174 <a class="dropdown-list-link dropdown-link-list-add-editdelete" href="#" onclick="' . htmlspecialchars($shortcut['action']) . ' return false;">' .
175 $shortcut['icon'] . ' ' .
176 htmlspecialchars($shortcut['label']) .
177 '</a>
178 ' . $editIcon . $deleteIcon . '
179 </li>';
180 }
181 $shortcutMenu[] = $shortcutGroup;
182 }
183 }
184 $shortcutMenu[] = '</ul>';
185
186 if (count($shortcutMenu) === 2) {
187 // No shortcuts added yet, show a small help message how to add shortcuts
188 $title = $languageService->sL('LLL:EXT:lang/locallang_core.xlf:toolbarItems.bookmarks', true);
189 $icon = '<span title="' . $title . '">' . $this->iconFactory->getIcon('actions-system-shortcut-new', Icon::SIZE_SMALL)->render('inline') . '</span>';
190 $label = str_replace('%icon%', $icon, $languageService->sL('LLL:EXT:lang/locallang_misc.xlf:bookmarkDescription', true));
191 $compiledShortcutMenu = '<p>' . $label . '</p>';
192 } else {
193 $compiledShortcutMenu = implode(LF, $shortcutMenu);
194 }
195
196 return $compiledShortcutMenu;
197 }
198
199 /**
200 * Renders the menu so that it can be returned as response to an AJAX call
201 *
202 * @param ServerRequestInterface $request
203 * @param ResponseInterface $response
204 * @return ResponseInterface
205 */
206 public function menuAction(ServerRequestInterface $request, ResponseInterface $response)
207 {
208 $menuContent = $this->getDropDown();
209
210 $response->getBody()->write($menuContent);
211 $response = $response->withHeader('Content-Type', 'text/html; charset=utf-8');
212 return $response;
213 }
214
215 /**
216 * This toolbar item needs no additional attributes
217 *
218 * @return array
219 */
220 public function getAdditionalAttributes()
221 {
222 return array();
223 }
224
225 /**
226 * This item has a drop down
227 *
228 * @return bool
229 */
230 public function hasDropDown()
231 {
232 return true;
233 }
234
235 /**
236 * Retrieves the shortcuts for the current user
237 *
238 * @return array Array of shortcuts
239 */
240 protected function initShortcuts()
241 {
242 $databaseConnection = $this->getDatabaseConnection();
243 $globalGroupIdList = implode(',', array_keys($this->getGlobalShortcutGroups()));
244 $backendUser = $this->getBackendUser();
245 $res = $databaseConnection->exec_SELECTquery(
246 '*',
247 'sys_be_shortcuts',
248 '(userid = ' . (int)$backendUser->user['uid'] . ' AND sc_group>=0) OR sc_group IN (' . $globalGroupIdList . ')',
249 '',
250 'sc_group,sorting'
251 );
252 // Traverse shortcuts
253 $lastGroup = 0;
254 $shortcuts = array();
255 while ($row = $databaseConnection->sql_fetch_assoc($res)) {
256 $shortcut = array('raw' => $row);
257
258 list($row['module_name'], $row['M_module_name']) = explode('|', $row['module_name']);
259
260 $queryParts = parse_url($row['url']);
261 $queryParameters = GeneralUtility::explodeUrl2Array($queryParts['query'], 1);
262 if ($row['module_name'] === 'xMOD_alt_doc.php' && is_array($queryParameters['edit'])) {
263 $shortcut['table'] = key($queryParameters['edit']);
264 $shortcut['recordid'] = key($queryParameters['edit'][$shortcut['table']]);
265 if ($queryParameters['edit'][$shortcut['table']][$shortcut['recordid']] === 'edit') {
266 $shortcut['type'] = 'edit';
267 } elseif ($queryParameters['edit'][$shortcut['table']][$shortcut['recordid']] === 'new') {
268 $shortcut['type'] = 'new';
269 }
270 if (substr($shortcut['recordid'], -1) === ',') {
271 $shortcut['recordid'] = substr($shortcut['recordid'], 0, -1);
272 }
273 } else {
274 $shortcut['type'] = 'other';
275 }
276 // Check for module access
277 $moduleName = $row['M_module_name'] ?: $row['module_name'];
278 if (!$backendUser->isAdmin()) {
279 if (!isset($this->getLanguageService()->moduleLabels['tabs_images'][$moduleName . '_tab'])) {
280 // Nice hack to check if the user has access to this module
281 // - otherwise the translation label would not have been loaded :-)
282 continue;
283 }
284 $pageId = $this->getLinkedPageId($row['url']);
285 if (MathUtility::canBeInterpretedAsInteger($pageId)) {
286 // Check for webmount access
287 if (!$backendUser->isInWebMount($pageId)) {
288 continue;
289 }
290 // Check for record access
291 $pageRow = BackendUtility::getRecord('pages', $pageId);
292 if (!$backendUser->doesUserHaveAccess($pageRow, ($perms = 1))) {
293 continue;
294 }
295 }
296 }
297 $moduleParts = explode('_', $moduleName);
298 $shortcutGroup = (int)$row['sc_group'];
299 if ($shortcutGroup && $lastGroup !== $shortcutGroup && $shortcutGroup !== self::SUPERGLOBAL_GROUP) {
300 $shortcut['groupLabel'] = $this->getShortcutGroupLabel($shortcutGroup);
301 }
302 $lastGroup = $shortcutGroup;
303
304 if ($row['description']) {
305 $shortcut['label'] = $row['description'];
306 } else {
307 $shortcut['label'] = GeneralUtility::fixed_lgd_cs(rawurldecode($queryParts['query']), 150);
308 }
309 $shortcut['group'] = $shortcutGroup;
310 $shortcut['icon'] = $this->getShortcutIcon($row, $shortcut);
311 $shortcut['iconTitle'] = $this->getShortcutIconTitle($shortcut['label'], $row['module_name'], $row['M_module_name']);
312 $shortcut['action'] = 'jump(' . GeneralUtility::quoteJSvalue($this->getTokenUrl($row['url'])) . ',' . GeneralUtility::quoteJSvalue($moduleName) . ',' . GeneralUtility::quoteJSvalue($moduleParts[0]) . ', ' . (int)$pageId . ');';
313
314 $shortcuts[] = $shortcut;
315 }
316 return $shortcuts;
317 }
318
319 /**
320 * Adds the correct token, if the url is an index.php script
321 *
322 * @param string $url
323 * @return string
324 */
325 protected function getTokenUrl($url)
326 {
327 $parsedUrl = parse_url($url);
328 parse_str($parsedUrl['query'], $parameters);
329
330 // parse the returnUrl and replace the module token of it
331 if (isset($parameters['returnUrl'])) {
332 $parsedReturnUrl = parse_url($parameters['returnUrl']);
333 parse_str($parsedReturnUrl['query'], $returnUrlParameters);
334 if (strpos($parsedReturnUrl['path'], 'index.php') !== false && isset($returnUrlParameters['M'])) {
335 $module = $returnUrlParameters['M'];
336 $returnUrl = BackendUtility::getModuleUrl($module, $returnUrlParameters);
337 $parameters['returnUrl'] = $returnUrl;
338 $url = $parsedUrl['path'] . '?' . http_build_query($parameters);
339 }
340 }
341
342 if (strpos($parsedUrl['path'], 'index.php') !== false && isset($parameters['M'])) {
343 $module = $parameters['M'];
344 $url = BackendUtility::getModuleUrl($module, $parameters);
345 } elseif (strpos($parsedUrl['path'], 'index.php') !== false && isset($parameters['route'])) {
346 $routePath = $parameters['route'];
347 /** @var \TYPO3\CMS\Backend\Routing\Router $router */
348 $router = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\Router::class);
349 try {
350 $route = $router->match($routePath);
351 if ($route) {
352 $routeIdentifier = $route->getOption('_identifier');
353 /** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
354 $uriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
355 unset($parameters['route']);
356 $url = (string)$uriBuilder->buildUriFromRoute($routeIdentifier, $parameters);
357 }
358 } catch (\TYPO3\CMS\Backend\Routing\Exception\ResourceNotFoundException $e) {
359 $url = '';
360 }
361 }
362 return $url;
363 }
364
365 /**
366 * Gets shortcuts for a specific group
367 *
368 * @param int $groupId Group Id
369 * @return array Array of shortcuts that matched the group
370 */
371 protected function getShortcutsByGroup($groupId)
372 {
373 $shortcuts = array();
374 foreach ($this->shortcuts as $shortcut) {
375 if ($shortcut['group'] == $groupId) {
376 $shortcuts[] = $shortcut;
377 }
378 }
379 return $shortcuts;
380 }
381
382 /**
383 * Gets a shortcut by its uid
384 *
385 * @param int $shortcutId Shortcut id to get the complete shortcut for
386 * @return mixed An array containing the shortcut's data on success or FALSE on failure
387 */
388 protected function getShortcutById($shortcutId)
389 {
390 $returnShortcut = false;
391 foreach ($this->shortcuts as $shortcut) {
392 if ($shortcut['raw']['uid'] == (int)$shortcutId) {
393 $returnShortcut = $shortcut;
394 continue;
395 }
396 }
397 return $returnShortcut;
398 }
399
400 /**
401 * Gets the available shortcut groups from default groups, user TSConfig, and global groups
402 *
403 * @return array
404 */
405 protected function initShortcutGroups()
406 {
407 $languageService = $this->getLanguageService();
408 $backendUser = $this->getBackendUser();
409 // Groups from TSConfig
410 $bookmarkGroups = $backendUser->getTSConfigProp('options.bookmarkGroups');
411 if (is_array($bookmarkGroups) && !empty($bookmarkGroups)) {
412 foreach ($bookmarkGroups as $groupId => $label) {
413 if (!empty($label)) {
414 $this->shortcutGroups[$groupId] = (string)$label;
415 } elseif ($backendUser->isAdmin()) {
416 unset($this->shortcutGroups[$groupId]);
417 }
418 }
419 }
420 // Generate global groups, all global groups have negative IDs.
421 if (!empty($this->shortcutGroups)) {
422 $groups = $this->shortcutGroups;
423 foreach ($groups as $groupId => $groupLabel) {
424 $this->shortcutGroups[$groupId * -1] = $groupLabel;
425 }
426 }
427 // Group -100 is kind of superglobal and can't be changed.
428 $this->shortcutGroups[self::SUPERGLOBAL_GROUP] = 1;
429 // Add labels
430 foreach ($this->shortcutGroups as $groupId => $groupLabel) {
431 $groupId = (int)$groupId;
432 $label = $groupLabel;
433 if ($groupLabel == '1') {
434 $label = $languageService->sL('LLL:EXT:lang/locallang_misc.xlf:bookmark_group_' . abs($groupId), true);
435 if (empty($label)) {
436 // Fallback label
437 $label = $languageService->getLL('bookmark_group', true) . ' ' . abs($groupId);
438 }
439 }
440 if ($groupId < 0) {
441 // Global group
442 $label = $languageService->sL('LLL:EXT:lang/locallang_misc.xlf:bookmark_global', true) . ': ' . (!empty($label) ? $label : abs($groupId));
443 if ($groupId === self::SUPERGLOBAL_GROUP) {
444 $label = $languageService->getLL('bookmark_global', true) . ': ' . $languageService->getLL('bookmark_all', true);
445 }
446 }
447 $this->shortcutGroups[$groupId] = $label;
448 }
449 return $this->shortcutGroups;
450 }
451
452 /**
453 * Fetches the available shortcut groups, renders a form so it can be saved later on, usually called via AJAX
454 *
455 * @param ServerRequestInterface $request
456 * @param ResponseInterface $response
457 * @return ResponseInterface the full HTML for the form
458 */
459 public function editFormAction(ServerRequestInterface $request, ResponseInterface $response)
460 {
461 $parsedBody = $request->getParsedBody();
462 $queryParams = $request->getQueryParams();
463
464 $selectedShortcutId = (int)(isset($parsedBody['shortcutId']) ? $parsedBody['shortcutId'] : $queryParams['shortcutId']);
465 $selectedShortcutGroupId = (int)(isset($parsedBody['shortcutGroup']) ? $parsedBody['shortcutGroup'] : $queryParams['shortcutGroup']);
466 $selectedShortcut = $this->getShortcutById($selectedShortcutId);
467
468 $shortcutGroups = $this->shortcutGroups;
469 if (!$this->getBackendUser()->isAdmin()) {
470 foreach ($shortcutGroups as $groupId => $groupName) {
471 if ((int)$groupId < 0) {
472 unset($shortcutGroups[$groupId]);
473 }
474 }
475 }
476
477 // build the form
478 $content = '
479 <form class="shortcut-form" role="form">
480 <div class="form-group">
481 <input type="text" class="form-control" name="shortcut-title" value="' . htmlspecialchars($selectedShortcut['label']) . '">
482 </div>';
483
484 $content .= '
485 <div class="form-group">
486 <select class="form-control" name="shortcut-group">';
487 foreach ($shortcutGroups as $shortcutGroupId => $shortcutGroupTitle) {
488 $content .= '<option value="' . (int)$shortcutGroupId . '"' . ($selectedShortcutGroupId == $shortcutGroupId ? ' selected="selected"' : '') . '>' . htmlspecialchars($shortcutGroupTitle) . '</option>';
489 }
490 $content .= '
491 </select>
492 </div>
493 <input type="button" class="btn btn-default shortcut-form-cancel" value="Cancel">
494 <input type="button" class="btn btn-success shortcut-form-save" value="Save">
495 </form>';
496
497 $response->getBody()->write($content);
498 $response = $response->withHeader('Content-Type', 'text/html; charset=utf-8');
499 return $response;
500 }
501
502 /**
503 * Deletes a shortcut through an AJAX call
504 *
505 * @param ServerRequestInterface $request
506 * @param ResponseInterface $response
507 * @return ResponseInterface
508 */
509 public function removeShortcutAction(ServerRequestInterface $request, ResponseInterface $response)
510 {
511 $parsedBody = $request->getParsedBody();
512 $queryParams = $request->getQueryParams();
513
514 $databaseConnection = $this->getDatabaseConnection();
515 $shortcutId = (int)(isset($parsedBody['shortcutId']) ? $parsedBody['shortcutId'] : $queryParams['shortcutId']);
516 $fullShortcut = $this->getShortcutById($shortcutId);
517 $success = false;
518 if ($fullShortcut['raw']['userid'] == $this->getBackendUser()->user['uid']) {
519 $databaseConnection->exec_DELETEquery('sys_be_shortcuts', 'uid = ' . $shortcutId);
520 if ($databaseConnection->sql_affected_rows() === 1) {
521 $success = true;
522 }
523 }
524 $response->getBody()->write(json_encode(['success' => $success]));
525 return $response;
526 }
527
528 /**
529 * Creates a shortcut through an AJAX call
530 *
531 * @param ServerRequestInterface $request
532 * @param ResponseInterface $response
533 * @return ResponseInterface
534 */
535 public function createShortcutAction(ServerRequestInterface $request, ResponseInterface $response)
536 {
537 $languageService = $this->getLanguageService();
538 $parsedBody = $request->getParsedBody();
539 $queryParams = $request->getQueryParams();
540
541 // Default name
542 $shortcutName = 'Shortcut';
543 $shortcutNamePrepend = '';
544 $url = isset($parsedBody['url']) ? $parsedBody['url'] : $queryParams['url'];
545
546 // Use given display name
547 if (!empty($parsedBody['displayName'])) {
548 $shortcutName = $parsedBody['displayName'];
549 }
550
551 // Determine shortcut type
552 $url = rawurldecode($url);
553 $queryParts = parse_url($url);
554 $queryParameters = GeneralUtility::explodeUrl2Array($queryParts['query'], true);
555
556 // Proceed only if no scheme is defined, as URL is expected to be relative
557 if (empty($queryParts['scheme'])) {
558 if (is_array($queryParameters['edit'])) {
559 $shortcut['table'] = key($queryParameters['edit']);
560 $shortcut['recordid'] = key($queryParameters['edit'][$shortcut['table']]);
561 $shortcut['pid'] = BackendUtility::getRecord($shortcut['table'], $shortcut['recordid'])['pid'];
562 if ($queryParameters['edit'][$shortcut['table']][$shortcut['recordid']] == 'edit') {
563 $shortcut['type'] = 'edit';
564 $shortcutNamePrepend = $languageService->getLL('shortcut_edit', true);
565 } elseif ($queryParameters['edit'][$shortcut['table']][$shortcut['recordid']] == 'new') {
566 $shortcut['type'] = 'new';
567 $shortcutNamePrepend = $languageService->getLL('shortcut_create', true);
568 }
569 } else {
570 $shortcut['type'] = 'other';
571 $shortcut['table'] = '';
572 $shortcut['recordid'] = 0;
573 }
574
575 // Check if given id is a combined identifier
576 if (!empty($queryParameters['id']) && preg_match('/^[0-9]+:/', $queryParameters['id'])) {
577 try {
578 $resourceFactory = ResourceFactory::getInstance();
579 $resource = $resourceFactory->getObjectFromCombinedIdentifier($queryParameters['id']);
580 $shortcutName = trim($shortcutNamePrepend . ' ' . $resource->getName());
581 } catch (ResourceDoesNotExistException $e) {
582 }
583 } else {
584 // Lookup the title of this page and use it as default description
585 $pageId = (int)($shortcut['pid'] ?: ($shortcut['recordid'] ?: $this->getLinkedPageId($url)));
586 $page = false;
587 if ($pageId) {
588 $page = BackendUtility::getRecord('pages', $pageId);
589 }
590 if (!empty($page)) {
591 // Set the name to the title of the page
592 if ($shortcut['type'] === 'other') {
593 if (empty($shortcutName)) {
594 $shortcutName = $page['title'];
595 } else {
596 $shortcutName .= ' (' . $page['title'] . ')';
597 }
598 } else {
599 $shortcutName = $shortcutNamePrepend . ' ' .
600 $languageService->sL($GLOBALS['TCA'][$shortcut['table']]['ctrl']['title']) .
601 ' (' . $page['title'] . ')';
602 }
603 } elseif ($shortcut['table'] !== '' && $shortcut['type'] !== 'other') {
604 $shortcutName = $shortcutNamePrepend . ' ' .
605 $languageService->sL($GLOBALS['TCA'][$shortcut['table']]['ctrl']['title']);
606 }
607 }
608
609 return $this->tryAddingTheShortcut($response, $url, $shortcutName);
610 }
611 }
612
613 /**
614 * Try to adding a shortcut
615 *
616 * @param ResponseInterface $response
617 * @param string $url
618 * @param string $shortcutName
619 * @return ResponseInterface
620 */
621 protected function tryAddingTheShortcut(ResponseInterface $response, $url, $shortcutName)
622 {
623 $module = GeneralUtility::_POST('module');
624 $shortcutCreated = 'failed';
625
626 if (!empty($module) && !empty($url)) {
627 $shortcutCreated = 'alreadyExists';
628
629 if (!BackendUtility::shortcutExists($url)) {
630 $shortcutCreated = $this->addShortcut($url, $shortcutName, $module);
631 }
632 }
633
634 $response->getBody()->write($shortcutCreated);
635 $response = $response->withHeader('Content-Type', 'text/html; charset=utf-8');
636 return $response;
637 }
638
639 /**
640 * Add a shortcut now with some user stuffs
641 *
642 * @param string $url
643 * @param string $shortcutName
644 * @param string $module
645 *
646 * @return string
647 */
648 protected function addShortcut($url, $shortcutName, $module)
649 {
650 // Shorts
651 $db = $this->getDatabaseConnection();
652 $lS = $this->getLanguageService();
653
654 if ($shortcutName === 'Shortcut' && !empty($lS->moduleLabels['labels'][$module . '_tablabel'])) {
655 $shortcutName = $lS->moduleLabels['labels'][$module . '_tablabel'];
656 }
657
658 $motherModule = GeneralUtility::_POST('motherModName');
659 $fieldValues = [
660 'userid' => $this->getBackendUser()->user['uid'],
661 'module_name' => $module . '|' . $motherModule,
662 'url' => $url,
663 'description' => $shortcutName,
664 'sorting' => $GLOBALS['EXEC_TIME']
665 ];
666
667 $db->exec_INSERTquery('sys_be_shortcuts', $fieldValues);
668
669 $shortcutCreated = 'failed';
670 if ($db->sql_affected_rows() === 1) {
671 $shortcutCreated = 'success';
672 }
673
674 return $shortcutCreated;
675 }
676
677 /**
678 * Exists already a shortcut entry for this TYPO3 url?
679 *
680 * @param string $url
681 *
682 * @return bool
683 */
684 protected function shortcutExists($url)
685 {
686 $statement = $this->getDatabaseConnection()->prepare_SELECTquery(
687 'uid',
688 'sys_be_shortcuts',
689 'userid = :userid AND url = :url'
690 );
691
692 $statement->bindValues([
693 ':userid' => $this->getBackendUser()->user['uid'],
694 ':url' => $url
695 ]);
696
697 $statement->execute();
698 $rows = $statement->fetch(PreparedStatement::FETCH_ASSOC);
699 $statement->free();
700
701 return !empty($rows);
702 }
703
704 /**
705 * Gets called when a shortcut is changed, checks whether the user has
706 * permissions to do so and saves the changes if everything is ok
707 *
708 * @param ServerRequestInterface $request
709 * @param ResponseInterface $response
710 * @return ResponseInterface
711 */
712 public function saveFormAction(ServerRequestInterface $request, ResponseInterface $response)
713 {
714 $parsedBody = $request->getParsedBody();
715 $queryParams = $request->getQueryParams();
716
717 $databaseConnection = $this->getDatabaseConnection();
718 $backendUser = $this->getBackendUser();
719 $shortcutId = (int)(isset($parsedBody['shortcutId']) ? $parsedBody['shortcutId'] : $queryParams['shortcutId']);
720 $shortcutName = strip_tags(isset($parsedBody['shortcutTitle']) ? $parsedBody['shortcutTitle'] : $queryParams['shortcutTitle']);
721 $shortcutGroupId = (int)(isset($parsedBody['shortcutGroup']) ? $parsedBody['shortcutGroup'] : $queryParams['shortcutGroup']);
722 // Users can only modify their own shortcuts (except admins)
723 $addUserWhere = !$backendUser->isAdmin() ? ' AND userid=' . (int)$backendUser->user['uid'] : '';
724 $fieldValues = array(
725 'description' => $shortcutName,
726 'sc_group' => $shortcutGroupId
727 );
728 if ($fieldValues['sc_group'] < 0 && !$backendUser->isAdmin()) {
729 $fieldValues['sc_group'] = 0;
730 }
731 $databaseConnection->exec_UPDATEquery('sys_be_shortcuts', 'uid=' . $shortcutId . $addUserWhere, $fieldValues);
732 $affectedRows = $databaseConnection->sql_affected_rows();
733 if ($affectedRows == 1) {
734 $response->getBody()->write($shortcutName);
735 } else {
736 $response->getBody()->write('failed');
737 }
738 return $response->withHeader('Content-Type', 'html');
739 }
740
741 /**
742 * Gets the label for a shortcut group
743 *
744 * @param int $groupId A shortcut group id
745 * @return string The shortcut group label, can be an empty string if no group was found for the id
746 */
747 protected function getShortcutGroupLabel($groupId)
748 {
749 return isset($this->shortcutGroups[$groupId]) ? $this->shortcutGroups[$groupId] : '';
750 }
751
752 /**
753 * Gets a list of global groups, shortcuts in these groups are available to all users
754 *
755 * @return array Array of global groups
756 */
757 protected function getGlobalShortcutGroups()
758 {
759 $globalGroups = array();
760 foreach ($this->shortcutGroups as $groupId => $groupLabel) {
761 if ($groupId < 0) {
762 $globalGroups[$groupId] = $groupLabel;
763 }
764 }
765 return $globalGroups;
766 }
767
768 /**
769 * runs through the available shortcuts an collects their groups
770 *
771 * @return array Array of groups which have shortcuts
772 */
773 protected function getGroupsFromShortcuts()
774 {
775 $groups = array();
776 foreach ($this->shortcuts as $shortcut) {
777 $groups[$shortcut['group']] = $this->shortcutGroups[$shortcut['group']];
778 }
779 return array_unique($groups);
780 }
781
782 /**
783 * Gets the icon for the shortcut
784 *
785 * @param array $row
786 * @param array $shortcut
787 * @return string Shortcut icon as img tag
788 */
789 protected function getShortcutIcon($row, $shortcut)
790 {
791 $databaseConnection = $this->getDatabaseConnection();
792 $languageService = $this->getLanguageService();
793 $titleAttribute = $languageService->sL('LLL:EXT:lang/locallang_core.xlf:toolbarItems.shortcut', true);
794 switch ($row['module_name']) {
795 case 'xMOD_alt_doc.php':
796 $table = $shortcut['table'];
797 $recordid = $shortcut['recordid'];
798 $icon = '';
799 if ($shortcut['type'] == 'edit') {
800 // Creating the list of fields to include in the SQL query:
801 $selectFields = $this->fieldArray;
802 $selectFields[] = 'uid';
803 $selectFields[] = 'pid';
804 if ($table == 'pages') {
805 $selectFields[] = 'module';
806 $selectFields[] = 'extendToSubpages';
807 $selectFields[] = 'doktype';
808 }
809 if (is_array($GLOBALS['TCA'][$table]['ctrl']['enablecolumns'])) {
810 $selectFields = array_merge($selectFields, $GLOBALS['TCA'][$table]['ctrl']['enablecolumns']);
811 }
812 if ($GLOBALS['TCA'][$table]['ctrl']['type']) {
813 $selectFields[] = $GLOBALS['TCA'][$table]['ctrl']['type'];
814 }
815 if ($GLOBALS['TCA'][$table]['ctrl']['typeicon_column']) {
816 $selectFields[] = $GLOBALS['TCA'][$table]['ctrl']['typeicon_column'];
817 }
818 if ($GLOBALS['TCA'][$table]['ctrl']['versioningWS']) {
819 $selectFields[] = 't3ver_state';
820 }
821 // Unique list!
822 $selectFields = array_unique($selectFields);
823 $permissionClause = $table === 'pages' && $this->perms_clause ? ' AND ' . $this->perms_clause : '';
824 $sqlQueryParts = array(
825 'SELECT' => implode(',', $selectFields),
826 'FROM' => $table,
827 'WHERE' => 'uid IN (' . $recordid . ') ' . $permissionClause . BackendUtility::deleteClause($table) . BackendUtility::versioningPlaceholderClause($table)
828 );
829 $result = $databaseConnection->exec_SELECT_queryArray($sqlQueryParts);
830 $row = $databaseConnection->sql_fetch_assoc($result);
831 $icon = '<span title="' . $titleAttribute . '">' . $this->iconFactory->getIconForRecord($table, (array)$row, Icon::SIZE_SMALL)->render() . '</span>';
832 } elseif ($shortcut['type'] == 'new') {
833 $icon = '<span title="' . $titleAttribute . '">' . $this->iconFactory->getIconForRecord($table, array(), Icon::SIZE_SMALL)->render() . '</span>';
834 }
835 break;
836 case 'file_edit':
837 $icon = '<span title="' . $titleAttribute . '">' . $this->iconFactory->getIcon('mimetypes-text-html', Icon::SIZE_SMALL)->render() . '</span>';
838 break;
839 case 'wizard_rte':
840 $icon = '<span title="' . $titleAttribute . '">' . $this->iconFactory->getIcon('mimetypes-word', Icon::SIZE_SMALL)->render() . '</span>';
841 break;
842 default:
843 if ($languageService->moduleLabels['tabs_images'][$row['module_name'] . '_tab']) {
844 $icon = $languageService->moduleLabels['tabs_images'][$row['module_name'] . '_tab'];
845 // Change icon of fileadmin references - otherwise it doesn't differ with Web->List
846 $icon = str_replace('mod/file/list/list.gif', 'mod/file/file.gif', $icon);
847 if (GeneralUtility::isAbsPath($icon)) {
848 $icon = '../' . PathUtility::stripPathSitePrefix($icon);
849 }
850 // @todo: hardcoded width as we don't have a way to address module icons with an API yet.
851 $icon = '<img src="' . htmlspecialchars($icon) . '" alt="' . $titleAttribute . '" width="16">';
852 } else {
853 $icon = '<span title="' . $titleAttribute . '">' . $this->iconFactory->getIcon('empty-empty', Icon::SIZE_SMALL)->render() . '</span>';
854 }
855 }
856 return $icon;
857 }
858
859 /**
860 * Returns title for the shortcut icon
861 *
862 * @param string $shortcutLabel Shortcut label
863 * @param string $moduleName Backend module name (key)
864 * @param string $parentModuleName Parent module label
865 * @return string Title for the shortcut icon
866 */
867 protected function getShortcutIconTitle($shortcutLabel, $moduleName, $parentModuleName = '')
868 {
869 $languageService = $this->getLanguageService();
870 if (substr($moduleName, 0, 5) == 'xMOD_') {
871 $title = substr($moduleName, 5);
872 } else {
873 $splitModuleName = explode('_', $moduleName);
874 $title = $languageService->moduleLabels['tabs'][$splitModuleName[0] . '_tab'];
875 if (count($splitModuleName) > 1) {
876 $title .= '>' . $languageService->moduleLabels['tabs'][$moduleName . '_tab'];
877 }
878 }
879 if ($parentModuleName) {
880 $title .= ' (' . $parentModuleName . ')';
881 }
882 $title .= ': ' . $shortcutLabel;
883 return $title;
884 }
885
886 /**
887 * Return the ID of the page in the URL if found.
888 *
889 * @param string $url The URL of the current shortcut link
890 * @return string If a page ID was found, it is returned. Otherwise: 0
891 */
892 protected function getLinkedPageId($url)
893 {
894 return preg_replace('/.*[\\?&]id=([^&]+).*/', '$1', $url);
895 }
896
897 /**
898 * Position relative to others, live search should be very right
899 *
900 * @return int
901 */
902 public function getIndex()
903 {
904 return 20;
905 }
906
907 /**
908 * Returns the current BE user.
909 *
910 * @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
911 */
912 protected function getBackendUser()
913 {
914 return $GLOBALS['BE_USER'];
915 }
916
917 /**
918 * Returns current PageRenderer
919 *
920 * @return PageRenderer
921 */
922 protected function getPageRenderer()
923 {
924 return GeneralUtility::makeInstance(PageRenderer::class);
925 }
926
927 /**
928 * Returns LanguageService
929 *
930 * @return \TYPO3\CMS\Lang\LanguageService
931 */
932 protected function getLanguageService()
933 {
934 return $GLOBALS['LANG'];
935 }
936
937 /**
938 * Return DatabaseConnection
939 *
940 * @return \TYPO3\CMS\Core\Database\DatabaseConnection
941 */
942 protected function getDatabaseConnection()
943 {
944 return $GLOBALS['TYPO3_DB'];
945 }
946 }