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