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