ShortcutToolbarItem.php 26.8 KB
Newer Older
1
<?php
2
namespace TYPO3\CMS\Backend\Backend\ToolbarItems;
3

4
/*
5
 * This file is part of the TYPO3 CMS project.
6
 *
7
8
9
 * It is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License, either version 2
 * of the License, or any later version.
10
 *
11
12
 * For the full copyright and license information, please read the
 * LICENSE.txt file that was distributed with this source code.
13
 *
14
15
 * The TYPO3 project - inspiring people to share!
 */
16

17
18
use TYPO3\CMS\Backend\Module\ModuleLoader;
use TYPO3\CMS\Backend\Toolbar\ToolbarItemInterface;
Nicole Cordes's avatar
Nicole Cordes committed
19
20
use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Backend\Utility\IconUtility;
21
22
use TYPO3\CMS\Core\Http\AjaxRequestHandler;
use TYPO3\CMS\Core\Page\PageRenderer;
Nicole Cordes's avatar
Nicole Cordes committed
23
24
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\MathUtility;
25
use TYPO3\CMS\Core\Utility\PathUtility;
Nicole Cordes's avatar
Nicole Cordes committed
26

27
28
29
/**
 * Class to render the shortcut menu
 */
30
class ShortcutToolbarItem implements ToolbarItemInterface {
31

32
	/**
33
	 * @const integer Number of super global group
34
	 */
35
	const SUPERGLOBAL_GROUP = -100;
36
37
38
39

	/**
	 * @var string
	 */
40
	public $perms_clause;
41
42
43
44
45

	/**
	 * @var array
	 */
	public $fieldArray;
46
47
48
49
50
51
52
53

	/**
	 * All available shortcuts
	 *
	 * @var array
	 */
	protected $shortcuts;

54
55
56
57
58
	/**
	 * @var array
	 */
	protected $shortcutGroups;

59
60
61
62
63
64
65
66
67
68
69
	/**
	 * Labels of all groups.
	 * If value is 1, the system will try to find a label in the locallang array.
	 *
	 * @var array
	 */
	protected $groupLabels;

	/**
	 * Constructor
	 */
70
	public function __construct() {
71
		if (TYPO3_REQUESTTYPE & TYPO3_REQUESTTYPE_AJAX) {
72
			$this->getLanguageService()->includeLLFile('EXT:lang/locallang_misc.xlf');
73
			// Needed to get the correct icons when reloading the menu after saving it
74
			$loadModules = GeneralUtility::makeInstance(ModuleLoader::class);
75
76
			$loadModules->load($GLOBALS['TBE_MODULES']);
		}
77

78
79
80
81
82
83
84
85
86
87
		// By default, 5 groups are set
		$this->shortcutGroups = array(
			1 => '1',
			2 => '1',
			3 => '1',
			4 => '1',
			5 => '1'
		);
		$this->shortcutGroups = $this->initShortcutGroups();
		$this->shortcuts = $this->initShortcuts();
88
89

		$this->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Backend/Toolbar/ShortcutMenu');
90
91
92
93
94
	}

	/**
	 * Checks whether the user has access to this toolbar item
	 *
95
	 * @return bool TRUE if user has access, FALSE if not
96
97
	 */
	public function checkAccess() {
98
		return (bool)$this->getBackendUser()->getTSConfigVal('options.enableBookmarks');
99
100
101
	}

	/**
102
	 * Render shortcut icon
103
	 *
104
105
106
107
108
109
110
111
112
	 * @return string HTML
	 */
	public function getItem() {
		return IconUtility::getSpriteIcon(
			'apps-toolbar-menu-shortcut',
			array(
				'title' => $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:toolbarItems.bookmarks', TRUE),
			)
		);
113
114
115
	}

	/**
116
	 * Render drop down content
117
	 *
118
119
120
121
122
123
124
	 * @return string HTML
	 */
	public function getDropDown() {
		$languageService = $this->getLanguageService();
		$shortcutGroup = $languageService->sL('LLL:EXT:lang/locallang_core.xlf:toolbarItems.bookmarksGroup', TRUE);
		$shortcutEdit = $languageService->sL('LLL:EXT:lang/locallang_core.xlf:toolbarItems.bookmarksEdit', TRUE);
		$shortcutDelete = $languageService->sL('LLL:EXT:lang/locallang_core.xlf:toolbarItems.bookmarksDelete', TRUE);
Benjamin Kott's avatar
Benjamin Kott committed
125
126
		$editIcon = '<a href="#" class="dropdown-list-link-edit shortcut-edit">' . IconUtility::getSpriteIcon('actions-document-open', array('title' => $shortcutEdit)) . '</a>';
		$deleteIcon = '<a href="#" class="dropdown-list-link-delete shortcut-delete">' . IconUtility::getSpriteIcon('actions-edit-delete', array('title' => $shortcutDelete)) . '</a>';
127

128
		$shortcutMenu[] = '<ul class="dropdown-list">';
129

130
131
132
		// Render shortcuts with no group (group id = 0) first
		$noGroupShortcuts = $this->getShortcutsByGroup(0);
		foreach ($noGroupShortcuts as $shortcut) {
133
134
135
136
137
138
139
140
141

			$shortcutMenu[] = '
				<li class="shortcut" data-shortcutid="' . (int)$shortcut['raw']['uid'] . '">
					<a class="dropdown-list-link dropdown-link-list-add-editdelete" href="#" onclick="' . htmlspecialchars($shortcut['action']) . ' return false;">' .
						$shortcut['icon'] . ' ' .
						htmlspecialchars($shortcut['label']) .
					'</a>
					' . $editIcon . $deleteIcon . '
				</li>';
142
143
144
145
146
		}
		// Now render groups and the contained shortcuts
		$groups = $this->getGroupsFromShortcuts();
		krsort($groups, SORT_NUMERIC);
		foreach ($groups as $groupId => $groupLabel) {
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
			if ($groupId != 0) {
				$shortcutGroup = '';
				if (count($shortcutMenu) > 1) {
					$shortcutGroup .= '<li class="divider"></li>';
				}
				$shortcutGroup .= '
					<li class="dropdown-header" id="shortcut-group-' . (int)$groupId . '">
						' . $groupLabel . '
					</li>';
				$shortcuts = $this->getShortcutsByGroup($groupId);
				$i = 0;
				foreach ($shortcuts as $shortcut) {
					$i++;
					$shortcutGroup .= '
					<li class="shortcut" data-shortcutid="' . (int)$shortcut['raw']['uid'] . '" data-shortcutgroup="' . (int)$groupId . '">
						<a class="dropdown-list-link dropdown-link-list-add-editdelete" href="#" onclick="' . htmlspecialchars($shortcut['action']) . ' return false;">' .
							$shortcut['icon'] . ' ' .
							htmlspecialchars($shortcut['label']) .
						'</a>
						' . $editIcon . $deleteIcon . '
					</li>';
				}
				$shortcutMenu[] = $shortcutGroup;
170
171
			}
		}
172
		$shortcutMenu[] = '</ul>';
173

174
		if (count($shortcutMenu) === 2) {
175
			// No shortcuts added yet, show a small help message how to add shortcuts
176
			$title = $languageService->sL('LLL:EXT:lang/locallang_core.xlf:toolbarItems.bookmarks', TRUE);
Nicole Cordes's avatar
Nicole Cordes committed
177
			$icon = IconUtility::getSpriteIcon('actions-system-shortcut-new', array(
178
179
				'title' => $title
			));
180
			$label = str_replace('%icon%', $icon, $languageService->sL('LLL:EXT:lang/locallang_misc.xlf:bookmarkDescription'));
181
182
183
			$compiledShortcutMenu = '<p>' . $label . '</p>';
		} else {
			$compiledShortcutMenu = implode(LF, $shortcutMenu);
184
		}
185

186
		return $compiledShortcutMenu;
187
188
189
190
191
192
	}

	/**
	 * Renders the menu so that it can be returned as response to an AJAX call
	 *
	 * @param array $params Array of parameters from the AJAX interface, currently unused
193
	 * @param \TYPO3\CMS\Core\Http\AjaxRequestHandler $ajaxObj Object of type AjaxRequestHandler
194
195
	 * @return void
	 */
196
	public function renderAjaxMenu($params = array(), AjaxRequestHandler $ajaxObj = NULL) {
197
		$menuContent = $this->getDropDown();
198
199
200
201
		$ajaxObj->addContent('shortcutMenu', $menuContent);
	}

	/**
202
	 * This toolbar item needs no additional attributes
203
204
205
	 *
	 * @return array
	 */
206
	public function getAdditionalAttributes() {
207
208
209
210
		return array();
	}

	/**
211
	 * This item has a drop down
212
213
214
	 *
	 * @return bool
	 */
215
	public function hasDropDown() {
216
		return TRUE;
217
218
219
220
221
222
223
224
	}

	/**
	 * Retrieves the shortcuts for the current user
	 *
	 * @return array Array of shortcuts
	 */
	protected function initShortcuts() {
225
		$databaseConnection = $this->getDatabaseConnection();
226
		$globalGroupIdList = implode(',', array_keys($this->getGlobalShortcutGroups()));
227
228
		$backendUser = $this->getBackendUser();
		$res = $databaseConnection->exec_SELECTquery(
229
230
			'*',
			'sys_be_shortcuts',
231
			'(userid = ' . (int)$backendUser->user['uid'] . ' AND sc_group>=0) OR sc_group IN (' . $globalGroupIdList . ')',
232
233
234
			'',
			'sc_group,sorting'
		);
235
		// Traverse shortcuts
236
237
		$lastGroup = 0;
		$shortcuts = array();
238
		while ($row = $databaseConnection->sql_fetch_assoc($res)) {
239
			$shortcut = array('raw' => $row);
240
241
242

			list($row['module_name'], $row['M_module_name']) = explode('|', $row['module_name']);

243
			$queryParts = parse_url($row['url']);
Nicole Cordes's avatar
Nicole Cordes committed
244
			$queryParameters = GeneralUtility::explodeUrl2Array($queryParts['query'], 1);
245
			if ($row['module_name'] === 'xMOD_alt_doc.php' && is_array($queryParameters['edit'])) {
246
247
				$shortcut['table'] = key($queryParameters['edit']);
				$shortcut['recordid'] = key($queryParameters['edit'][$shortcut['table']]);
248
				if ($queryParameters['edit'][$shortcut['table']][$shortcut['recordid']] === 'edit') {
249
					$shortcut['type'] = 'edit';
250
				} elseif ($queryParameters['edit'][$shortcut['table']][$shortcut['recordid']] === 'new') {
251
252
					$shortcut['type'] = 'new';
				}
253
				if (substr($shortcut['recordid'], -1) === ',') {
254
255
256
257
258
259
					$shortcut['recordid'] = substr($shortcut['recordid'], 0, -1);
				}
			} else {
				$shortcut['type'] = 'other';
			}
			// Check for module access
260
			$moduleName = $row['M_module_name'] ?: $row['module_name'];
261
			$pageId = $this->getLinkedPageId($row['url']);
262
263
			if (!$backendUser->isAdmin()) {
				if (!isset($this->getLanguageService()->moduleLabels['tabs_images'][$moduleName . '_tab'])) {
264
265
266
267
					// Nice hack to check if the user has access to this module
					// - otherwise the translation label would not have been loaded :-)
					continue;
				}
Nicole Cordes's avatar
Nicole Cordes committed
268
				if (MathUtility::canBeInterpretedAsInteger($pageId)) {
269
					// Check for webmount access
270
					if (!$backendUser->isInWebMount($pageId)) {
271
272
273
						continue;
					}
					// Check for record access
Nicole Cordes's avatar
Nicole Cordes committed
274
					$pageRow = BackendUtility::getRecord('pages', $pageId);
275
					if (!$backendUser->doesUserHaveAccess($pageRow, ($perms = 1))) {
276
277
278
279
						continue;
					}
				}
			}
280
281
282
			$moduleParts = explode('_', $moduleName);
			$shortcutGroup = (int)$row['sc_group'];
			if ($shortcutGroup && $lastGroup !== $shortcutGroup && $shortcutGroup !== self::SUPERGLOBAL_GROUP) {
283
284
				$shortcut['groupLabel'] = $this->getShortcutGroupLabel($shortcutGroup);
			}
285
286
			$lastGroup = $shortcutGroup;

287
288
289
			if ($row['description']) {
				$shortcut['label'] = $row['description'];
			} else {
Nicole Cordes's avatar
Nicole Cordes committed
290
				$shortcut['label'] = GeneralUtility::fixed_lgd_cs(rawurldecode($queryParts['query']), 150);
291
292
293
			}
			$shortcut['group'] = $shortcutGroup;
			$shortcut['icon'] = $this->getShortcutIcon($row, $shortcut);
294
			$shortcut['iconTitle'] = $this->getShortcutIconTitle($shortcut['label'], $row['module_name'], $row['M_module_name']);
295
			$shortcut['action'] = 'jump(' . GeneralUtility::quoteJSvalue($this->getTokenUrl($row['url'])) . ',' . GeneralUtility::quoteJSvalue($moduleName) . ',' . GeneralUtility::quoteJSvalue($moduleParts[0]) . ', ' . (int)$pageId . ');';
296

297
298
299
300
301
			$shortcuts[] = $shortcut;
		}
		return $shortcuts;
	}

302
	/**
303
	 * Adds the correct token, if the url is an index.php script
304
305
306
307
308
309
310
	 *
	 * @param string $url
	 * @return string
	 */
	protected function getTokenUrl($url) {
		$parsedUrl = parse_url($url);
		parse_str($parsedUrl['query'], $parameters);
311
312
313
314
315

		// parse the returnUrl and replace the module token of it
		if (isset($parameters['returnUrl'])) {
			$parsedReturnUrl = parse_url($parameters['returnUrl']);
			parse_str($parsedReturnUrl['query'], $returnUrlParameters);
316
			if (strpos($parsedReturnUrl['path'], 'index.php') !== FALSE && isset($returnUrlParameters['M'])) {
317
318
319
320
321
322
323
				$module = $returnUrlParameters['M'];
				$returnUrl = BackendUtility::getModuleUrl($module, $returnUrlParameters);
				$parameters['returnUrl'] = $returnUrl;
				$url = $parsedUrl['path'] . '?' . http_build_query($parameters);
			}
		}

324
		if (strpos($parsedUrl['path'], 'index.php') !== FALSE && isset($parameters['M'])) {
325
			$module = $parameters['M'];
326
			$url = BackendUtility::getModuleUrl($module, $parameters);
327
328
329
330
		}
		return $url;
	}

331
332
333
	/**
	 * Gets shortcuts for a specific group
	 *
334
	 * @param int $groupId Group Id
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
	 * @return array Array of shortcuts that matched the group
	 */
	protected function getShortcutsByGroup($groupId) {
		$shortcuts = array();
		foreach ($this->shortcuts as $shortcut) {
			if ($shortcut['group'] == $groupId) {
				$shortcuts[] = $shortcut;
			}
		}
		return $shortcuts;
	}

	/**
	 * Gets a shortcut by its uid
	 *
350
	 * @param int $shortcutId Shortcut id to get the complete shortcut for
351
352
353
354
355
	 * @return mixed An array containing the shortcut's data on success or FALSE on failure
	 */
	protected function getShortcutById($shortcutId) {
		$returnShortcut = FALSE;
		foreach ($this->shortcuts as $shortcut) {
356
			if ($shortcut['raw']['uid'] == (int)$shortcutId) {
357
358
359
360
361
362
363
364
				$returnShortcut = $shortcut;
				continue;
			}
		}
		return $returnShortcut;
	}

	/**
365
	 * Gets the available shortcut groups from default groups, user TSConfig, and global groups
366
367
368
	 *
	 * @return array
	 */
369
	protected function initShortcutGroups() {
370
371
		$languageService = $this->getLanguageService();
		$backendUser = $this->getBackendUser();
372
		// Groups from TSConfig
373
		$bookmarkGroups = $backendUser->getTSConfigProp('options.bookmarkGroups');
374
		if (is_array($bookmarkGroups) && !empty($bookmarkGroups)) {
375
			foreach ($bookmarkGroups as $groupId => $label) {
376
377
				if (!empty($label)) {
					$this->shortcutGroups[$groupId] = (string)$label;
378
				} elseif ($backendUser->isAdmin()) {
379
380
381
382
383
					unset($this->shortcutGroups[$groupId]);
				}
			}
		}
		// Generate global groups, all global groups have negative IDs.
384
		if (!empty($this->shortcutGroups)) {
385
386
387
388
389
390
			$groups = $this->shortcutGroups;
			foreach ($groups as $groupId => $groupLabel) {
				$this->shortcutGroups[$groupId * -1] = $groupLabel;
			}
		}
		// Group -100 is kind of superglobal and can't be changed.
391
		$this->shortcutGroups[self::SUPERGLOBAL_GROUP] = 1;
392
393
		// Add labels
		foreach ($this->shortcutGroups as $groupId => $groupLabel) {
394
			$groupId = (int)$groupId;
395
396
			$label = $groupLabel;
			if ($groupLabel == '1') {
397
				$label = $languageService->sL('LLL:EXT:lang/locallang_misc.xlf:bookmark_group_' . abs($groupId), TRUE);
398
399
				if (empty($label)) {
					// Fallback label
400
					$label = $languageService->getLL('bookmark_group', TRUE) . ' ' . abs($groupId);
401
402
403
404
				}
			}
			if ($groupId < 0) {
				// Global group
405
				$label = $languageService->sL('LLL:EXT:lang/locallang_misc.xlf:bookmark_global', TRUE) . ': ' . (!empty($label) ? $label : abs($groupId));
406
				if ($groupId === self::SUPERGLOBAL_GROUP) {
407
					$label = $languageService->getLL('bookmark_global', TRUE) . ': ' . $languageService->getLL('bookmark_all', TRUE);
408
409
410
411
412
413
414
415
				}
			}
			$this->shortcutGroups[$groupId] = $label;
		}
		return $this->shortcutGroups;
	}

	/**
416
	 * gets the available shortcut groups, renders a form so it can be saved lateron
417
418
	 *
	 * @param array $params Array of parameters from the AJAX interface, currently unused
419
	 * @param \TYPO3\CMS\Core\Http\AjaxRequestHandler $ajaxObj Object of type AjaxRequestHandler
420
421
	 * @return void
	 */
422
	public function getAjaxShortcutEditForm($params = array(), AjaxRequestHandler $ajaxObj = NULL) {
423
424
425
426
		$selectedShortcutId = (int)GeneralUtility::_GP('shortcutId');
		$selectedShortcutGroupId = (int)GeneralUtility::_GP('shortcutGroup');
		$selectedShortcut = $this->getShortcutById($selectedShortcutId);

427
		$shortcutGroups = $this->shortcutGroups;
428
		if (!$this->getBackendUser()->isAdmin()) {
429
			foreach ($shortcutGroups as $groupId => $groupName) {
430
				if ((int)$groupId < 0) {
431
432
433
434
					unset($shortcutGroups[$groupId]);
				}
			}
		}
435
436

		// build the form
Benjamin Kott's avatar
Benjamin Kott committed
437
438
439
440
441
442
443
444
445
		$content = '
			<form class="shortcut-form" role="form">
				<div class="form-group">
					<input type="text" class="form-control" name="shortcut-title" value="' . htmlspecialchars($selectedShortcut['label']) . '">
				</div>';

		$content .= '
				<div class="form-group">
					<select class="form-control" name="shortcut-group">';
446
447
448
		foreach ($shortcutGroups as $shortcutGroupId => $shortcutGroupTitle) {
			$content .= '<option value="' . (int)$shortcutGroupId . '"' . ($selectedShortcutGroupId == $shortcutGroupId ? ' selected="selected"' : '') . '>' . htmlspecialchars($shortcutGroupTitle) . '</option>';
		}
Benjamin Kott's avatar
Benjamin Kott committed
449
450
451
452
453
454
		$content .= '
					</select>
				</div>
				<input type="button" class="btn btn-default shortcut-form-cancel" value="Cancel">
				<input type="button" class="btn btn-success shortcut-form-save" value="Save">
			</form>';
455
456

		$ajaxObj->addContent('data', $content);
457
458
459
460
461
462
	}

	/**
	 * Deletes a shortcut through an AJAX call
	 *
	 * @param array $params Array of parameters from the AJAX interface, currently unused
463
	 * @param \TYPO3\CMS\Core\Http\AjaxRequestHandler $ajaxObj Object of type AjaxRequestHandler
464
465
	 * @return void
	 */
466
	public function deleteAjaxShortcut($params = array(), AjaxRequestHandler $ajaxObj = NULL) {
467
		$databaseConnection = $this->getDatabaseConnection();
468
		$shortcutId = (int)GeneralUtility::_POST('shortcutId');
469
470
		$fullShortcut = $this->getShortcutById($shortcutId);
		$ajaxReturn = 'failed';
471
472
473
		if ($fullShortcut['raw']['userid'] == $this->getBackendUser()->user['uid']) {
			$databaseConnection->exec_DELETEquery('sys_be_shortcuts', 'uid = ' . $shortcutId);
			if ($databaseConnection->sql_affected_rows() == 1) {
474
475
476
477
478
479
480
481
482
483
				$ajaxReturn = 'deleted';
			}
		}
		$ajaxObj->addContent('delete', $ajaxReturn);
	}

	/**
	 * Creates a shortcut through an AJAX call
	 *
	 * @param array $params Array of parameters from the AJAX interface, currently unused
484
	 * @param \TYPO3\CMS\Core\Http\AjaxRequestHandler $ajaxObj Oject of type AjaxRequestHandler
485
486
	 * @return void
	 */
487
	public function createAjaxShortcut($params = array(), AjaxRequestHandler $ajaxObj = NULL) {
488
489
		$databaseConnection = $this->getDatabaseConnection();
		$languageService = $this->getLanguageService();
490
491
492
493
		$shortcutCreated = 'failed';
		// Default name
		$shortcutName = 'Shortcut';
		$shortcutNamePrepend = '';
Nicole Cordes's avatar
Nicole Cordes committed
494
495
496
		$url = GeneralUtility::_POST('url');
		$module = GeneralUtility::_POST('module');
		$motherModule = GeneralUtility::_POST('motherModName');
497
		// Determine shortcut type
498
		$url = rawurldecode($url);
499
		$queryParts = parse_url($url);
500
		$queryParameters = GeneralUtility::explodeUrl2Array($queryParts['query'], TRUE);
501
502
503
504
505
506
507
		// Proceed only if no scheme is defined, as URL is expected to be relative
		if (empty($queryParts['scheme'])) {
			if (is_array($queryParameters['edit'])) {
				$shortcut['table'] = key($queryParameters['edit']);
				$shortcut['recordid'] = key($queryParameters['edit'][$shortcut['table']]);
				if ($queryParameters['edit'][$shortcut['table']][$shortcut['recordid']] == 'edit') {
					$shortcut['type'] = 'edit';
508
					$shortcutNamePrepend = $languageService->getLL('shortcut_edit', TRUE);
509
510
				} elseif ($queryParameters['edit'][$shortcut['table']][$shortcut['recordid']] == 'new') {
					$shortcut['type'] = 'new';
511
					$shortcutNamePrepend = $languageService->getLL('shortcut_create', TRUE);
512
513
514
515
516
517
				}
			} else {
				$shortcut['type'] = 'other';
			}
			// Lookup the title of this page and use it as default description
			$pageId = $shortcut['recordid'] ? $shortcut['recordid'] : $this->getLinkedPageId($url);
Nicole Cordes's avatar
Nicole Cordes committed
518
519
			if (MathUtility::canBeInterpretedAsInteger($pageId)) {
				$page = BackendUtility::getRecord('pages', $pageId);
520
				if (!empty($page)) {
521
522
523
524
					// Set the name to the title of the page
					if ($shortcut['type'] == 'other') {
						$shortcutName = $page['title'];
					} else {
525
						$shortcutName = $shortcutNamePrepend . ' ' . $languageService->sL($GLOBALS['TCA'][$shortcut['table']]['ctrl']['title']) . ' (' . $page['title'] . ')';
526
527
528
529
530
531
532
533
534
535
536
537
538
539
					}
				}
			} else {
				$dirName = urldecode($pageId);
				if (preg_match('/\\/$/', $dirName)) {
					// If $pageId is a string and ends with a slash,
					// assume it is a fileadmin reference and set
					// the description to the basename of that path
					$shortcutName .= ' ' . basename($dirName);
				}
			}
			// adding the shortcut
			if ($module && $url) {
				$fieldValues = array(
540
					'userid' => $this->getBackendUser()->user['uid'],
541
					'module_name' => $module . '|' . $motherModule,
542
543
544
545
					'url' => $url,
					'description' => $shortcutName,
					'sorting' => $GLOBALS['EXEC_TIME']
				);
546
547
				$databaseConnection->exec_INSERTquery('sys_be_shortcuts', $fieldValues);
				if ($databaseConnection->sql_affected_rows() == 1) {
548
549
550
551
552
553
554
555
556
557
558
559
					$shortcutCreated = 'success';
				}
			}
			$ajaxObj->addContent('create', $shortcutCreated);
		}
	}

	/**
	 * Gets called when a shortcut is changed, checks whether the user has
	 * permissions to do so and saves the changes if everything is ok
	 *
	 * @param array $params Array of parameters from the AJAX interface, currently unused
560
	 * @param \TYPO3\CMS\Core\Http\AjaxRequestHandler $ajaxObj Object of type AjaxRequestHandler
561
562
	 * @return void
	 */
563
	public function setAjaxShortcut($params = array(), AjaxRequestHandler $ajaxObj = NULL) {
564
565
		$databaseConnection = $this->getDatabaseConnection();
		$backendUser = $this->getBackendUser();
566
		$shortcutId = (int)GeneralUtility::_POST('shortcutId');
567
568
		$shortcutName = strip_tags(GeneralUtility::_POST('shortcutTitle'));
		$shortcutGroupId = (int)GeneralUtility::_POST('shortcutGroup');
569
		if ($shortcutGroupId > 0 || $backendUser->isAdmin()) {
570
			// Users can delete only their own shortcuts (except admins)
571
			$addUserWhere = !$backendUser->isAdmin() ? ' AND userid=' . (int)$backendUser->user['uid'] : '';
572
573
574
575
			$fieldValues = array(
				'description' => $shortcutName,
				'sc_group' => $shortcutGroupId
			);
576
			if ($fieldValues['sc_group'] < 0 && !$backendUser->isAdmin()) {
577
578
				$fieldValues['sc_group'] = 0;
			}
579
580
			$databaseConnection->exec_UPDATEquery('sys_be_shortcuts', 'uid=' . $shortcutId . $addUserWhere, $fieldValues);
			$affectedRows = $databaseConnection->sql_affected_rows();
581
582
583
584
585
586
587
588
589
590
591
592
			if ($affectedRows == 1) {
				$ajaxObj->addContent('shortcut', $shortcutName);
			} else {
				$ajaxObj->addContent('shortcut', 'failed');
			}
		}
		$ajaxObj->setContentFormat('plain');
	}

	/**
	 * Gets the label for a shortcut group
	 *
593
	 * @param int $groupId A shortcut group id
594
595
596
	 * @return string The shortcut group label, can be an empty string if no group was found for the id
	 */
	protected function getShortcutGroupLabel($groupId) {
597
		return isset($this->shortcutGroups[$groupId]) ? $this->shortcutGroups[$groupId] : '';
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
	}

	/**
	 * Gets a list of global groups, shortcuts in these groups are available to all users
	 *
	 * @return array Array of global groups
	 */
	protected function getGlobalShortcutGroups() {
		$globalGroups = array();
		foreach ($this->shortcutGroups as $groupId => $groupLabel) {
			if ($groupId < 0) {
				$globalGroups[$groupId] = $groupLabel;
			}
		}
		return $globalGroups;
	}

	/**
	 * runs through the available shortcuts an collects their groups
	 *
	 * @return array Array of groups which have shortcuts
	 */
	protected function getGroupsFromShortcuts() {
		$groups = array();
		foreach ($this->shortcuts as $shortcut) {
			$groups[$shortcut['group']] = $this->shortcutGroups[$shortcut['group']];
		}
		return array_unique($groups);
	}

	/**
	 * Gets the icon for the shortcut
	 *
	 * @param array $row
	 * @param array $shortcut
	 * @return string Shortcut icon as img tag
	 */
	protected function getShortcutIcon($row, $shortcut) {
636
637
		$databaseConnection = $this->getDatabaseConnection();
		$languageService = $this->getLanguageService();
638
		$titleAttribute = $languageService->sL('LLL:EXT:lang/locallang_core.xlf:toolbarItems.shortcut', TRUE);
639
		switch ($row['module_name']) {
640
641
642
			case 'xMOD_alt_doc.php':
				$table = $shortcut['table'];
				$recordid = $shortcut['recordid'];
643
				$icon = '';
644
645
646
647
648
649
				if ($shortcut['type'] == 'edit') {
					// Creating the list of fields to include in the SQL query:
					$selectFields = $this->fieldArray;
					$selectFields[] = 'uid';
					$selectFields[] = 'pid';
					if ($table == 'pages') {
650
651
						$selectFields[] = 'module';
						$selectFields[] = 'extendToSubpages';
652
						$selectFields[] = 'doktype';
653
					}
654
655
656
657
658
659
660
661
662
663
664
665
666
667
					if (is_array($GLOBALS['TCA'][$table]['ctrl']['enablecolumns'])) {
						$selectFields = array_merge($selectFields, $GLOBALS['TCA'][$table]['ctrl']['enablecolumns']);
					}
					if ($GLOBALS['TCA'][$table]['ctrl']['type']) {
						$selectFields[] = $GLOBALS['TCA'][$table]['ctrl']['type'];
					}
					if ($GLOBALS['TCA'][$table]['ctrl']['typeicon_column']) {
						$selectFields[] = $GLOBALS['TCA'][$table]['ctrl']['typeicon_column'];
					}
					if ($GLOBALS['TCA'][$table]['ctrl']['versioningWS']) {
						$selectFields[] = 't3ver_state';
					}
					// Unique list!
					$selectFields = array_unique($selectFields);
668
					$permissionClause = $table === 'pages' && $this->perms_clause ? ' AND ' . $this->perms_clause : '';
669
670
671
672
673
					$sqlQueryParts = array(
						'SELECT' => implode(',', $selectFields),
						'FROM' => $table,
						'WHERE' => 'uid IN (' . $recordid . ') ' . $permissionClause . BackendUtility::deleteClause($table) . BackendUtility::versioningPlaceholderClause($table)
					);
674
675
					$result = $databaseConnection->exec_SELECT_queryArray($sqlQueryParts);
					$row = $databaseConnection->sql_fetch_assoc($result);
676
					$icon = IconUtility::getSpriteIconForRecord($table, (array)$row, array('title' => $titleAttribute));
677
				} elseif ($shortcut['type'] == 'new') {
678
					$icon = IconUtility::getSpriteIconForRecord($table, array(), array('title' => $titleAttribute));
679
				}
680
				break;
681
			case 'file_edit':
682
				$icon = IconUtility::getSpriteIcon('mimetypes-text-html', array('title' => $titleAttribute));
683
				break;
684
			case 'wizard_rte':
685
				$icon = IconUtility::getSpriteIcon('mimetypes-word', array('title' => $titleAttribute));
686
687
				break;
			default:
688
689
				if ($languageService->moduleLabels['tabs_images'][$row['module_name'] . '_tab']) {
					$icon = $languageService->moduleLabels['tabs_images'][$row['module_name'] . '_tab'];
690
691
692
					// Change icon of fileadmin references - otherwise it doesn't differ with Web->List
					$icon = str_replace('mod/file/list/list.gif', 'mod/file/file.gif', $icon);
					if (GeneralUtility::isAbsPath($icon)) {
693
						$icon = '../' . PathUtility::stripPathSitePrefix($icon);
694
					}
695
696
					// @todo: hardcoded width as we don't have a way to address module icons with an API yet.
					$icon = '<img src="' . htmlspecialchars($icon) . '" alt="' . $titleAttribute . '" width="16">';
697
				} else {
698
					$icon = IconUtility::getSpriteIcon('empty-empty', array('title' => $titleAttribute));
699
700
				}
		}
701
		return $icon;
702
703
704
705
706
707
708
709
710
711
712
	}

	/**
	 * Returns title for the shortcut icon
	 *
	 * @param string $shortcutLabel Shortcut label
	 * @param string $moduleName Backend module name (key)
	 * @param string $parentModuleName Parent module label
	 * @return string Title for the shortcut icon
	 */
	protected function getShortcutIconTitle($shortcutLabel, $moduleName, $parentModuleName = '') {
713
		$languageService = $this->getLanguageService();
714
715
716
717
		if (substr($moduleName, 0, 5) == 'xMOD_') {
			$title = substr($moduleName, 5);
		} else {
			$splitModuleName = explode('_', $moduleName);
718
			$title = $languageService->moduleLabels['tabs'][$splitModuleName[0] . '_tab'];
719
			if (count($splitModuleName) > 1) {
720
				$title .= '>' . $languageService->moduleLabels['tabs'][($moduleName . '_tab')];
721
722
723
			}
		}
		if ($parentModuleName) {
724
			$title .= ' (' . $parentModuleName . ')';
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
		}
		$title .= ': ' . $shortcutLabel;
		return $title;
	}

	/**
	 * Return the ID of the page in the URL if found.
	 *
	 * @param string $url The URL of the current shortcut link
	 * @return string If a page ID was found, it is returned. Otherwise: 0
	 */
	protected function getLinkedPageId($url) {
		return preg_replace('/.*[\\?&]id=([^&]+).*/', '$1', $url);
	}

740
741
742
743
744
745
746
747
748
	/**
	 * Position relative to others, live search should be very right
	 *
	 * @return int
	 */
	public function getIndex() {
		return 20;
	}

749
750
751
752
753
754
755
756
757
758
759
760
	/**
	 * Returns the current BE user.
	 *
	 * @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
	 */
	protected function getBackendUser() {
		return $GLOBALS['BE_USER'];
	}

	/**
	 * Returns current PageRenderer
	 *
761
	 * @return PageRenderer
762
763
	 */
	protected function getPageRenderer() {
764
		return GeneralUtility::makeInstance(PageRenderer::class);
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
	}

	/**
	 * Returns LanguageService
	 *
	 * @return \TYPO3\CMS\Lang\LanguageService
	 */
	protected function getLanguageService() {
		return $GLOBALS['LANG'];
	}

	/**
	 * Return DatabaseConnection
	 *
	 * @return \TYPO3\CMS\Core\Database\DatabaseConnection
	 */
	protected function getDatabaseConnection() {
		return $GLOBALS['TYPO3_DB'];
	}

785
}