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