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