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