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