‪TYPO3CMS  ‪main
ShortcutRepository.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
19 
20 use Symfony\Component\DependencyInjection\Attribute\Autoconfigure;
24 use TYPO3\CMS\Backend\Utility\BackendUtility;
29 use TYPO3\CMS\Core\Imaging\IconSize;
36 
42 #[Autoconfigure(public: true)]
44 {
48  protected const ‪SUPERGLOBAL_GROUP = -100;
49 
50  protected const ‪TABLE_NAME = 'sys_be_shortcuts';
51 
52  protected array ‪$shortcuts;
53 
54  protected array ‪$shortcutGroups;
55 
56  public function ‪__construct(
57  protected readonly ‪ConnectionPool $connectionPool,
58  protected readonly ‪IconFactory $iconFactory,
59  protected readonly ‪ModuleProvider $moduleProvider,
60  protected readonly ‪Router $router,
61  protected readonly ‪UriBuilder $uriBuilder,
62  ) {
63  $this->shortcutGroups = $this->‪initShortcutGroups();
64  $this->shortcuts = $this->‪initShortcuts();
65  }
66 
73  public function ‪getShortcutById(int $shortcutId)
74  {
75  foreach ($this->shortcuts as $shortcut) {
76  if ($shortcut['raw']['uid'] === $shortcutId) {
77  return $shortcut;
78  }
79  }
80 
81  return false;
82  }
83 
90  public function ‪getShortcutsByGroup(int $groupId): array
91  {
92  ‪$shortcuts = [];
93 
94  foreach ($this->shortcuts as $shortcut) {
95  if ($shortcut['group'] === $groupId) {
96  ‪$shortcuts[] = $shortcut;
97  }
98  }
99 
100  return ‪$shortcuts;
101  }
102 
106  public function ‪getShortcutGroups(): array
107  {
109 
110  if (!$this->‪getBackendUser()->isAdmin()) {
111  foreach (‪$shortcutGroups as $groupId => $groupName) {
112  if ((int)$groupId < 0) {
113  unset(‪$shortcutGroups[$groupId]);
114  }
115  }
116  }
117 
118  return ‪$shortcutGroups;
119  }
120 
126  public function ‪getGroupsFromShortcuts(): array
127  {
128  $groups = [];
129 
130  foreach ($this->shortcuts as $shortcut) {
131  $groups[$shortcut['group']] = $this->shortcutGroups[$shortcut['group']] ?? '';
132  }
133 
134  return array_unique($groups);
135  }
136 
140  public function ‪shortcutExists(string $routeIdentifier, string $arguments): bool
141  {
142  $queryBuilder = $this->connectionPool->getQueryBuilderForTable(self::TABLE_NAME);
143  $queryBuilder->getRestrictions()->removeAll();
144 
145  ‪$uid = $queryBuilder->select('uid')
146  ->from(self::TABLE_NAME)
147  ->where(
148  $queryBuilder->expr()->eq(
149  'userid',
150  $queryBuilder->createNamedParameter($this->getBackendUser()->user['uid'], ‪Connection::PARAM_INT)
151  ),
152  $queryBuilder->expr()->eq('route', $queryBuilder->createNamedParameter($routeIdentifier)),
153  $queryBuilder->expr()->eq('arguments', $queryBuilder->createNamedParameter($arguments))
154  )
155  ->executeQuery()
156  ->fetchOne();
157 
158  return (bool)‪$uid;
159  }
160 
169  public function ‪addShortcut(string $routeIdentifier, string $arguments = '', string $title = ''): bool
170  {
171  // Do not add shortcuts for routes which do not exist
172  if (!$this->router->hasRoute($routeIdentifier)) {
173  return false;
174  }
175 
176  $queryBuilder = $this->connectionPool->getQueryBuilderForTable(self::TABLE_NAME);
177  $affectedRows = $queryBuilder
178  ->insert(self::TABLE_NAME)
179  ->values([
180  'userid' => $this->‪getBackendUser()->user['uid'],
181  'route' => $routeIdentifier,
182  'arguments' => $arguments,
183  'description' => $title ?: 'Shortcut', // Fall back to "Shortcut", see: initShortcuts()
184  'sorting' => ‪$GLOBALS['EXEC_TIME'],
185  ])
186  ->executeStatement();
187 
188  return $affectedRows === 1;
189  }
190 
198  public function ‪updateShortcut(int $id, string $title, int $groupId): bool
199  {
200  $backendUser = $this->‪getBackendUser();
201  $queryBuilder = $this->connectionPool->getQueryBuilderForTable(self::TABLE_NAME);
202  $queryBuilder->update(self::TABLE_NAME)
203  ->where(
204  $queryBuilder->expr()->eq(
205  'uid',
206  $queryBuilder->createNamedParameter($id, ‪Connection::PARAM_INT)
207  )
208  )
209  ->set('description', $title)
210  ->set('sc_group', $groupId);
211 
212  if (!$backendUser->isAdmin()) {
213  // Users can only modify their own shortcuts
214  $queryBuilder->andWhere(
215  $queryBuilder->expr()->eq(
216  'userid',
217  $queryBuilder->createNamedParameter($backendUser->user['uid'], ‪Connection::PARAM_INT)
218  )
219  );
220 
221  if ($groupId < 0) {
222  $queryBuilder->set('sc_group', 0);
223  }
224  }
225 
226  $affectedRows = $queryBuilder->executeStatement();
227 
228  return $affectedRows === 1;
229  }
230 
236  public function ‪removeShortcut(int $id): bool
237  {
238  $shortcut = $this->‪getShortcutById($id);
239  $success = false;
240 
241  if ((int)$shortcut['raw']['userid'] === (int)$this->‪getBackendUser()->user['uid']) {
242  $queryBuilder = $this->connectionPool->getQueryBuilderForTable(self::TABLE_NAME);
243  $affectedRows = $queryBuilder->delete(self::TABLE_NAME)
244  ->where(
245  $queryBuilder->expr()->eq(
246  'uid',
247  $queryBuilder->createNamedParameter($id, ‪Connection::PARAM_INT)
248  )
249  )
250  ->executeStatement();
251 
252  if ($affectedRows === 1) {
253  $success = true;
254  }
255  }
256 
257  return $success;
258  }
259 
263  protected function ‪initShortcutGroups(): array
264  {
265  $languageService = $this->‪getLanguageService();
266  $backendUser = $this->‪getBackendUser();
267  // By default, 5 groups are set
268  $shortcutGroups = [
269  1 => '1',
270  2 => '1',
271  3 => '1',
272  4 => '1',
273  5 => '1',
274  ];
275 
276  // Groups from TSConfig
277  $bookmarkGroups = $backendUser->getTSConfig()['options.']['bookmarkGroups.'] ?? [];
278 
279  if (is_array($bookmarkGroups)) {
280  foreach ($bookmarkGroups as $groupId => $label) {
281  if (!empty($label)) {
282  $label = (string)$label;
283  ‪$shortcutGroups[$groupId] = $languageService->sL($label);
284  } elseif ($backendUser->isAdmin()) {
285  unset(‪$shortcutGroups[$groupId]);
286  }
287  }
288  }
289 
290  // Generate global groups, all global groups have negative IDs.
291  if (!empty(‪$shortcutGroups)) {
292  foreach (‪$shortcutGroups as $groupId => $groupLabel) {
293  ‪$shortcutGroups[$groupId * -1] = $groupLabel;
294  }
295  }
296 
297  // Group -100 is kind of superglobal and can't be changed.
299 
300  // Add labels
301  $languageFile = 'LLL:EXT:core/Resources/Private/Language/locallang_misc.xlf';
302 
303  foreach (‪$shortcutGroups as $groupId => $groupLabel) {
304  $groupId = (int)$groupId;
305  $label = $groupLabel;
306 
307  if ($groupLabel === '1') {
308  $label = $languageService->sL($languageFile . ':bookmark_group_' . abs($groupId));
309 
310  if (empty($label)) {
311  // Fallback label
312  $label = $languageService->sL($languageFile . ':bookmark_group') . ' ' . abs($groupId);
313  }
314  }
315 
316  if ($groupId < 0) {
317  // Global group
318  $label = $languageService->sL($languageFile . ':bookmark_global') . ': ' . (!empty($label) ? $label : abs($groupId));
319 
320  if ($groupId === self::SUPERGLOBAL_GROUP) {
321  $label = $languageService->sL($languageFile . ':bookmark_global') . ': ' . $languageService->sL($languageFile . ':bookmark_all');
322  }
323  }
324 
325  ‪$shortcutGroups[$groupId] = htmlspecialchars($label);
326  }
327 
328  return ‪$shortcutGroups;
329  }
330 
336  protected function ‪initShortcuts(): array
337  {
338  $backendUser = $this->‪getBackendUser();
339  $lastGroup = 0;
340  ‪$shortcuts = [];
341 
342  $queryBuilder = $this->connectionPool->getQueryBuilderForTable(self::TABLE_NAME);
343  $result = $queryBuilder->select('*')
344  ->from(self::TABLE_NAME)
345  ->where(
346  $queryBuilder->expr()->and(
347  $queryBuilder->expr()->eq(
348  'userid',
349  $queryBuilder->createNamedParameter($backendUser->user['uid'], ‪Connection::PARAM_INT)
350  ),
351  $queryBuilder->expr()->gte(
352  'sc_group',
353  $queryBuilder->createNamedParameter(0, ‪Connection::PARAM_INT)
354  )
355  )
356  )
357  ->orWhere(
358  $queryBuilder->expr()->in(
359  'sc_group',
360  $queryBuilder->createNamedParameter(
361  array_keys($this->‪getGlobalShortcutGroups()),
363  )
364  )
365  )
366  ->orderBy('sc_group')
367  ->addOrderBy('sorting')
368  ->executeQuery();
369 
370  while ($row = $result->fetchAssociative()) {
371  $pageId = 0;
372  $shortcut = ['raw' => $row];
373  $routeIdentifier = $row['route'] ?? '';
374  $arguments = json_decode($row['arguments'] ?? '', true) ?? [];
375 
376  if ($routeIdentifier === 'record_edit' && is_array($arguments['edit'])) {
377  $shortcut['table'] = (string)(key($arguments['edit']) ?? '');
378  $shortcut['recordid'] = key($arguments['edit'][$shortcut['table']]);
379 
380  if ($arguments['edit'][$shortcut['table']][$shortcut['recordid']] === 'edit') {
381  $shortcut['type'] = 'edit';
382  } elseif ($arguments['edit'][$shortcut['table']][$shortcut['recordid']] === 'new') {
383  $shortcut['type'] = 'new';
384  }
385 
386  if (str_ends_with((string)$shortcut['recordid'], ',')) {
387  $shortcut['recordid'] = substr((string)$shortcut['recordid'], 0, -1);
388  }
389  } else {
390  $shortcut['type'] = 'other';
391  }
392 
393  $moduleName = $this->‪getModuleNameFromRouteIdentifier($routeIdentifier);
394 
395  // Skip shortcut if module name can not be resolved
396  if ($moduleName === '') {
397  continue;
398  }
399 
400  // Check if the user has access to this module
401  // @todo Hack for EditDocumentController / FormEngine, see issues #91368 and #91210
402  if ($routeIdentifier !== 'record_edit' && !$this->moduleProvider->accessGranted($moduleName, $backendUser)) {
403  continue;
404  }
405 
406  if ($moduleName === 'file_FilelistList' || $moduleName === 'media_management') {
407  $combinedIdentifier = (string)($arguments['id'] ?? '');
408  if ($combinedIdentifier !== '') {
409  $storage = GeneralUtility::makeInstance(StorageRepository::class)->findByCombinedIdentifier($combinedIdentifier);
410  if ($storage === null || $storage->isFallbackStorage()) {
411  // Continue, if invalid storage or disallowed fallback storage
412  continue;
413  }
414  $folderIdentifier = substr($combinedIdentifier, strpos($combinedIdentifier, ':') + 1);
415  try {
416  // By using $storage->getFolder() we implicitly check whether the folder
417  // still exists and the user has necessary permissions to access it.
418  $storage->getFolder($folderIdentifier);
420  // Continue, since current user does not have access to the folder
421  continue;
422  } catch (‪FolderDoesNotExistException $e) {
423  // Folder does not longer exists. However, the shortcut
424  // is still displayed, allowing the user to remove it.
425  }
426  }
427  } else {
428  if ($moduleName === 'record_edit' && isset($shortcut['table'], $shortcut['recordid'])) {
429  // Check if user is allowed to modify the requested record
430  if (!$backendUser->check('tables_modify', $shortcut['table'])) {
431  continue;
432  }
433  if ($shortcut['type'] === 'edit'
434  || ($shortcut['type'] === 'new' && (int)$shortcut['recordid'] < 0)
435  ) {
436  ‪$record = BackendUtility::getRecord($shortcut['table'], abs((int)$shortcut['recordid']));
437  // Check if requested record exists
438  if (‪$record === null || ‪$record === []) {
439  continue;
440  }
441  // Store the page id of the record in question
442  $pageId = ($shortcut['table'] === 'pages' ? (int)(‪$record['uid'] ?? 0) : (int)(‪$record['pid'] ?? 0));
443  } elseif ($shortcut['type'] === 'new' && (int)$shortcut['recordid'] > 0) {
444  // If type is new and "recordid" is positive, it references the current page
445  $pageId = (int)$shortcut['recordid'];
446  }
447  } else {
448  // In case this is no record edit shortcut, treat a possible "id" as page id
449  $pageId = (int)($arguments['id'] ?? 0);
450  }
451  if ($pageId > 0 && !$backendUser->isAdmin()) {
452  // Check for webmount access
453  if ($backendUser->isInWebMount($pageId) === null) {
454  continue;
455  }
456  // Check for record access
457  $pageRow = BackendUtility::getRecord('pages', $pageId);
458  if ($pageRow === null || !$backendUser->doesUserHaveAccess($pageRow, ‪Permission::PAGE_SHOW)) {
459  continue;
460  }
461  }
462  }
463 
464  $shortcutGroup = (int)$row['sc_group'];
465  if ($shortcutGroup && $lastGroup !== $shortcutGroup && $shortcutGroup !== self::SUPERGLOBAL_GROUP) {
466  $shortcut['groupLabel'] = $this->‪getShortcutGroupLabel($shortcutGroup);
467  }
468  $lastGroup = $shortcutGroup;
469 
470  $shortcut['group'] = $shortcutGroup;
471  $shortcut['icon'] = $this->‪getShortcutIcon($routeIdentifier, $moduleName, $shortcut);
472  $shortcut['label'] = ($row['description'] ?? false) ?: 'Shortcut'; // Fall back to "Shortcut", see: addShortcut()
473  $shortcut['href'] = (string)$this->uriBuilder->buildUriFromRoute($routeIdentifier, $arguments);
474  $shortcut['route'] = $routeIdentifier;
475  $shortcut['module'] = $moduleName;
476  $shortcut['pageId'] = $pageId;
477  ‪$shortcuts[] = $shortcut;
478  }
479 
480  return ‪$shortcuts;
481  }
482 
488  protected function ‪getGlobalShortcutGroups(): array
489  {
490  $globalGroups = [];
491 
492  foreach ($this->shortcutGroups as $groupId => $groupLabel) {
493  if ($groupId < 0) {
494  $globalGroups[$groupId] = $groupLabel;
495  }
496  }
497 
498  return $globalGroups;
499  }
500 
507  protected function ‪getShortcutGroupLabel(int $groupId): string
508  {
509  return $this->shortcutGroups[$groupId] ?? '';
510  }
511 
517  protected function ‪getShortcutIcon(string $routeIdentifier, string $moduleName, array $shortcut): string
518  {
519  switch ($routeIdentifier) {
520  case 'record_edit':
521  $table = $shortcut['table'];
522  $recordid = $shortcut['recordid'];
523  $icon = '';
524 
525  if ($shortcut['type'] === 'edit') {
526  $row = BackendUtility::getRecordWSOL($table, $recordid) ?? [];
527  $icon = $this->iconFactory->getIconForRecord($table, $row, IconSize::SMALL)->render();
528  } elseif ($shortcut['type'] === 'new') {
529  $icon = $this->iconFactory->getIconForRecord($table, [], IconSize::SMALL)->render();
530  }
531  break;
532  case 'file_edit':
533  $icon = $this->iconFactory->getIcon('mimetypes-text-html', IconSize::SMALL)->render();
534  break;
535  default:
536  $iconIdentifier = '';
537  if ($module = $this->moduleProvider->getModule($moduleName, null, false)) {
538  $iconIdentifier = $module->getIconIdentifier();
539  if ($iconIdentifier === '' && $module->getParentModule()) {
540  $iconIdentifier = $module->getParentModule()->getIconIdentifier();
541  }
542  }
543  if ($iconIdentifier === '') {
544  $iconIdentifier = 'empty-empty';
545  }
546  $icon = $this->iconFactory->getIcon($iconIdentifier, IconSize::SMALL)->render();
547  }
548 
549  return $icon;
550  }
551 
555  protected function ‪getModuleNameFromRouteIdentifier(string $routeIdentifier): string
556  {
557  if ($this->‪isSpecialRoute($routeIdentifier)) {
558  return $routeIdentifier;
559  }
560 
561  return (string)($this->router->getRoute($routeIdentifier)?->getOption('module')?->getIdentifier() ?? '');
562  }
563 
567  protected function ‪isSpecialRoute(string $routeIdentifier): bool
568  {
569  return in_array($routeIdentifier, ['record_edit', 'file_edit'], true);
570  }
571 
573  {
574  return ‪$GLOBALS['BE_USER'];
575  }
576 
578  {
579  return ‪$GLOBALS['LANG'];
580  }
581 }
‪TYPO3\CMS\Backend\Backend\Shortcut\ShortcutRepository
Definition: ShortcutRepository.php:44
‪TYPO3\CMS\Backend\Backend\Shortcut\ShortcutRepository\initShortcutGroups
‪initShortcutGroups()
Definition: ShortcutRepository.php:263
‪TYPO3\CMS\Backend\Backend\Shortcut\ShortcutRepository\getShortcutIcon
‪string getShortcutIcon(string $routeIdentifier, string $moduleName, array $shortcut)
Definition: ShortcutRepository.php:517
‪TYPO3\CMS\Core\Database\Connection\PARAM_INT
‪const PARAM_INT
Definition: Connection.php:52
‪TYPO3\CMS\Backend\Backend\Shortcut\ShortcutRepository\initShortcuts
‪array initShortcuts()
Definition: ShortcutRepository.php:336
‪TYPO3\CMS\Backend\Backend\Shortcut\ShortcutRepository\shortcutExists
‪shortcutExists(string $routeIdentifier, string $arguments)
Definition: ShortcutRepository.php:140
‪TYPO3\CMS\Core\Resource\Exception\InsufficientFolderAccessPermissionsException
Definition: InsufficientFolderAccessPermissionsException.php:23
‪TYPO3\CMS\Backend\Backend\Shortcut\ShortcutRepository\$shortcuts
‪array $shortcuts
Definition: ShortcutRepository.php:52
‪TYPO3\CMS\Backend\Backend\Shortcut\ShortcutRepository\isSpecialRoute
‪isSpecialRoute(string $routeIdentifier)
Definition: ShortcutRepository.php:567
‪TYPO3\CMS\Backend\Backend\Shortcut\ShortcutRepository\getLanguageService
‪getLanguageService()
Definition: ShortcutRepository.php:577
‪TYPO3\CMS\Core\Imaging\IconFactory
Definition: IconFactory.php:35
‪TYPO3\CMS\Backend\Backend\Shortcut
Definition: ShortcutRepository.php:18
‪TYPO3\CMS\Backend\Backend\Shortcut\ShortcutRepository\getShortcutGroups
‪getShortcutGroups()
Definition: ShortcutRepository.php:106
‪TYPO3\CMS\Backend\Module\ModuleProvider
Definition: ModuleProvider.php:29
‪TYPO3\CMS\Backend\Backend\Shortcut\ShortcutRepository\removeShortcut
‪removeShortcut(int $id)
Definition: ShortcutRepository.php:236
‪TYPO3\CMS\Backend\Backend\Shortcut\ShortcutRepository\getGlobalShortcutGroups
‪array getGlobalShortcutGroups()
Definition: ShortcutRepository.php:488
‪TYPO3\CMS\Core\Type\Bitmask\Permission
Definition: Permission.php:26
‪TYPO3\CMS\Backend\Backend\Shortcut\ShortcutRepository\addShortcut
‪addShortcut(string $routeIdentifier, string $arguments='', string $title='')
Definition: ShortcutRepository.php:169
‪TYPO3\CMS\Backend\Backend\Shortcut\ShortcutRepository\getShortcutsByGroup
‪array getShortcutsByGroup(int $groupId)
Definition: ShortcutRepository.php:90
‪TYPO3\CMS\Backend\Backend\Shortcut\ShortcutRepository\getModuleNameFromRouteIdentifier
‪getModuleNameFromRouteIdentifier(string $routeIdentifier)
Definition: ShortcutRepository.php:555
‪TYPO3\CMS\Backend\Backend\Shortcut\ShortcutRepository\__construct
‪__construct(protected readonly ConnectionPool $connectionPool, protected readonly IconFactory $iconFactory, protected readonly ModuleProvider $moduleProvider, protected readonly Router $router, protected readonly UriBuilder $uriBuilder,)
Definition: ShortcutRepository.php:56
‪TYPO3\CMS\Backend\Backend\Shortcut\ShortcutRepository\updateShortcut
‪updateShortcut(int $id, string $title, int $groupId)
Definition: ShortcutRepository.php:198
‪TYPO3\CMS\Backend\Routing\UriBuilder
Definition: UriBuilder.php:44
‪TYPO3\CMS\Core\Resource\Exception\FolderDoesNotExistException
Definition: FolderDoesNotExistException.php:21
‪TYPO3\CMS\Backend\Backend\Shortcut\ShortcutRepository\getShortcutById
‪mixed getShortcutById(int $shortcutId)
Definition: ShortcutRepository.php:73
‪TYPO3\CMS\Backend\Backend\Shortcut\ShortcutRepository\getGroupsFromShortcuts
‪array getGroupsFromShortcuts()
Definition: ShortcutRepository.php:126
‪TYPO3\CMS\Core\Resource\StorageRepository
Definition: StorageRepository.php:38
‪TYPO3\CMS\Webhooks\Message\$record
‪identifier readonly int readonly array $record
Definition: PageModificationMessage.php:36
‪TYPO3\CMS\Core\Authentication\BackendUserAuthentication
Definition: BackendUserAuthentication.php:62
‪TYPO3\CMS\Core\Type\Bitmask\Permission\PAGE_SHOW
‪const PAGE_SHOW
Definition: Permission.php:35
‪TYPO3\CMS\Core\Database\Connection
Definition: Connection.php:41
‪TYPO3\CMS\Backend\Backend\Shortcut\ShortcutRepository\SUPERGLOBAL_GROUP
‪const SUPERGLOBAL_GROUP
Definition: ShortcutRepository.php:48
‪TYPO3\CMS\Webhooks\Message\$uid
‪identifier readonly int $uid
Definition: PageModificationMessage.php:35
‪TYPO3\CMS\Backend\Backend\Shortcut\ShortcutRepository\getShortcutGroupLabel
‪string getShortcutGroupLabel(int $groupId)
Definition: ShortcutRepository.php:507
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Core\Localization\LanguageService
Definition: LanguageService.php:46
‪TYPO3\CMS\Core\Database\ConnectionPool
Definition: ConnectionPool.php:46
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Backend\Backend\Shortcut\ShortcutRepository\getBackendUser
‪getBackendUser()
Definition: ShortcutRepository.php:572
‪TYPO3\CMS\Backend\Routing\Router
Definition: Router.php:40
‪TYPO3\CMS\Core\Database\Connection\PARAM_INT_ARRAY
‪const PARAM_INT_ARRAY
Definition: Connection.php:72
‪TYPO3\CMS\Backend\Backend\Shortcut\ShortcutRepository\$shortcutGroups
‪array $shortcutGroups
Definition: ShortcutRepository.php:54
‪TYPO3\CMS\Backend\Backend\Shortcut\ShortcutRepository\TABLE_NAME
‪const TABLE_NAME
Definition: ShortcutRepository.php:50