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