Commit e4833fda authored by Oliver Bartsch's avatar Oliver Bartsch Committed by Christian Kuhn
Browse files

[!!!][TASK] Rework shortcut PHP API functionality

To be able to introduce URL rewrites for the backend,
the internal handling and registration of the shortcut
PHP API is reworked.

The Shortcut PHP API previously has the full URL of
the shortcut target stored in the database. This lead
to many problems such as shortcuts got invalid as soon
as their target module changed its route path. Furthermore,
this required unnecessary functionality like replacing
tokens on URL creation.

Therefore, a shortcut record now stores only the route
identifier of the module to link to and necessary arguments
in two new database columns. A upgrade wizard is in place
to migrate existing data.

The rework also required to deprecate some methods in
the ShortcutButton API and a parameter signature change
of the JavaScript function `TYPO3.ShortcutMenu.createShortcut()`
which performs the AJAX call to create new shortcuts.

Side effect, this also deprecated the last remains of
xMOD_alt_doc.php in the core.

Resolves: #93093
Releases: master
Change-Id: I07666a299651e4953b4adf2987fcd3469094c288
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/67143


Tested-by: default avatarTYPO3com <noreply@typo3.com>
Tested-by: Daniel Goerz's avatarDaniel Goerz <daniel.goerz@posteo.de>
Tested-by: Christian Kuhn's avatarChristian Kuhn <lolli@schwarzbu.ch>
Reviewed-by: Daniel Goerz's avatarDaniel Goerz <daniel.goerz@posteo.de>
Reviewed-by: Christian Kuhn's avatarChristian Kuhn <lolli@schwarzbu.ch>
parent 62e46dd3
......@@ -49,20 +49,18 @@ class ShortcutMenu {
* makes a call to the backend class to create a new shortcut,
* when finished it reloads the menu
*
* @param {String} moduleName
* @param {String} url
* @param {String} routeIdentifier
* @param {String} routeArguments
* @param {String} displayName
* @param {String} confirmationText
* @param {String} motherModule
* @param {Object} shortcutButton
* @param {String} displayName
*/
public createShortcut(
moduleName: string,
url: string,
routeIdentifier: string,
routeArguments: string,
displayName: string,
confirmationText: string,
motherModule: string,
shortcutButton: JQuery,
displayName: string,
): void {
if (typeof confirmationText !== 'undefined') {
Modal.confirm(TYPO3.lang['bookmark.create'], confirmationText).on('confirm.button.ok', (e: JQueryEventObject): void => {
......@@ -74,9 +72,8 @@ class ShortcutMenu {
});
(new AjaxRequest(TYPO3.settings.ajaxUrls.shortcut_create)).post({
module: moduleName,
url: url,
motherModName: motherModule,
routeIdentifier: routeIdentifier,
arguments: routeArguments,
displayName: displayName,
}).then((): void => {
this.refreshMenu();
......
......@@ -1788,7 +1788,6 @@ class EditDocumentController
if ($this->returnUrl !== $this->getCloseUrl()) {
$queryParams = $request->getQueryParams();
$potentialArguments = [
'returnUrl',
'edit',
'defVals',
'overrideVals',
......@@ -1806,7 +1805,7 @@ class EditDocumentController
}
$shortCutButton = $this->moduleTemplate->getDocHeaderComponent()->getButtonBar()->makeShortcutButton();
$shortCutButton
->setModuleName('xMOD_alt_doc.php')
->setRouteIdentifier('record_edit')
->setDisplayName($this->getShortcutTitle($request))
->setArguments($arguments);
$buttonBar->addButton($shortCutButton, $position, $group);
......
......@@ -174,10 +174,9 @@ class HelpController
$action = $request->getQueryParams()['action'] ?? $request->getParsedBody()['action'] ?? 'index';
$shortcutButton = $buttonBar->makeShortcutButton()
->setModuleName('help_cshmanual')
->setRouteIdentifier('help_cshmanual')
->setDisplayName($this->getShortcutTitle($request))
->setArguments([
'route' => $request->getQueryParams()['route'],
'action' => $action,
'table' => $request->getQueryParams()['table'] ?? '',
'field' => $request->getQueryParams()['field'] ?? ''
......
......@@ -764,10 +764,9 @@ class PageLayoutController
}
// Shortcut
$shortcutButton = $this->buttonBar->makeShortcutButton()
->setModuleName($this->moduleName)
->setRouteIdentifier($this->moduleName)
->setDisplayName($this->getShortcutTitle())
->setArguments([
'route' => $request->getQueryParams()['route'],
'id' => (int)$this->id,
'SET' => [
'tt_content_showHidden' => (bool)$this->MOD_SETTINGS['tt_content_showHidden'],
......
......@@ -83,16 +83,16 @@ class ShortcutController
$result = 'success';
$parsedBody = $request->getParsedBody();
$queryParams = $request->getQueryParams();
$url = rawurldecode($parsedBody['url'] ?? $queryParams['url'] ?? '');
$routeIdentifier = $parsedBody['routeIdentifier'] ?? $queryParams['routeIdentifier'] ?? '';
$arguments = $parsedBody['arguments'] ?? $queryParams['arguments'] ?? '';
if ($this->shortcutRepository->shortcutExists($url)) {
if ($routeIdentifier === '') {
$result = 'missingRoute';
} elseif ($this->shortcutRepository->shortcutExists($routeIdentifier, $arguments)) {
$result = 'alreadyExists';
} else {
$moduleName = $parsedBody['module'] ?? '';
$parentModuleName = $parsedBody['motherModName'] ?? '';
$shortcutName = $parsedBody['displayName'] ?? '';
$success = $this->shortcutRepository->addShortcut($url, $moduleName, $parentModuleName, $shortcutName);
$shortcutName = $parsedBody['displayName'] ?? $queryParams['arguments'] ?? '';
$success = $this->shortcutRepository->addShortcut($routeIdentifier, $arguments, $shortcutName);
if (!$success) {
$result = 'failed';
}
......
......@@ -640,7 +640,7 @@ class SiteConfigurationController
->setIcon($iconFactory->getIcon('actions-refresh', Icon::SIZE_SMALL));
$buttonBar->addButton($reloadButton, ButtonBar::BUTTON_POSITION_RIGHT);
$shortcutButton = $buttonBar->makeShortcutButton()
->setModuleName('site_configuration')
->setRouteIdentifier('site_configuration')
->setDisplayName($this->getLanguageService()->sL('LLL:EXT:backend/Resources/Private/Language/locallang_siteconfiguration_module.xlf:mlang_labels_tablabel'))
->setArguments([
'route' => $route
......
......@@ -16,6 +16,7 @@
namespace TYPO3\CMS\Backend\Template\Components\Buttons\Action;
use TYPO3\CMS\Backend\Backend\Shortcut\ShortcutRepository;
use TYPO3\CMS\Backend\Routing\Router;
use TYPO3\CMS\Backend\Template\Components\ButtonBar;
use TYPO3\CMS\Backend\Template\Components\Buttons\ButtonInterface;
use TYPO3\CMS\Backend\Template\Components\Buttons\PositionInterface;
......@@ -25,7 +26,6 @@ use TYPO3\CMS\Core\Imaging\Icon;
use TYPO3\CMS\Core\Imaging\IconFactory;
use TYPO3\CMS\Core\Localization\LanguageService;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\HttpUtility;
/**
* ShortcutButton
......@@ -36,17 +36,25 @@ use TYPO3\CMS\Core\Utility\HttpUtility;
* EXAMPLE USAGE TO ADD A SHORTCUT BUTTON:
*
* $buttonBar = $this->moduleTemplate->getDocHeaderComponent()->getButtonBar();
* $pageId = (int)($request->getQueryParams()['id'] ?? 0);
* $myButton = $buttonBar->makeShortcutButton()
* ->setRouteIdentifier('web_view')
* ->setDisplayName('View page ' . $pageId)
* ->setArguments([
* 'route' => $request->getQueryParams()['route']
* ])
* ->setModuleName('my_info');
* 'id' => $pageId
* ]);
* $buttonBar->addButton($myButton);
*/
class ShortcutButton implements ButtonInterface, PositionInterface
{
/**
* @var string The route identifier of the shortcut
*/
protected string $routeIdentifier = '';
/**
* @var string
* @deprecated since v11, will be removed in v12
*/
protected $moduleName = '';
......@@ -72,13 +80,37 @@ class ShortcutButton implements ButtonInterface, PositionInterface
*/
protected $getVariables = [];
/**
* Gets the route identifier for the shortcut.
*
* @return string
*/
public function getRouteIdentifier(): string
{
return $this->routeIdentifier;
}
/**
* Sets the route identifier for the shortcut.
*
* @param string $routeIdentifier
* @return ShortcutButton
*/
public function setRouteIdentifier(string $routeIdentifier): self
{
$this->routeIdentifier = $routeIdentifier;
return $this;
}
/**
* Gets the name of the module.
*
* @return string
* @deprecated since v11, will be removed in v12
*/
public function getModuleName()
{
trigger_error('Method getModuleName() is deprecated and will be removed in v12. Use getRouteIdentifier() instead.', E_USER_DEPRECATED);
return $this->moduleName;
}
......@@ -87,9 +119,11 @@ class ShortcutButton implements ButtonInterface, PositionInterface
*
* @param string $moduleName
* @return ShortcutButton
* @deprecated since v11, will be removed in v12
*/
public function setModuleName($moduleName)
{
trigger_error('Method setModuleName() is deprecated and will be removed in v12. Use setRouteIdentifier() instead.', E_USER_DEPRECATED);
$this->moduleName = $moduleName;
return $this;
}
......@@ -213,7 +247,7 @@ class ShortcutButton implements ButtonInterface, PositionInterface
*/
public function isValid()
{
return !empty($this->moduleName);
return $this->moduleName !== '' || $this->routeIdentifier !== '' || (string)($this->arguments['route'] ?? '') !== '';
}
/**
......@@ -237,7 +271,7 @@ class ShortcutButton implements ButtonInterface, PositionInterface
if ($this->displayName === '') {
trigger_error('Creating a shortcut button without a display name is deprecated and fallbacks will be removed in v12. Please use ShortcutButton->setDisplayName() to set a display name.', E_USER_DEPRECATED);
}
if (!empty($this->arguments)) {
if (!empty($this->routeIdentifier) || !empty($this->arguments)) {
$shortcutMarkup = $this->createShortcutMarkup();
} else {
// @deprecated since v11, the else branch will be removed in v12. Deprecation thrown by makeShortcutIcon() below
......@@ -261,36 +295,124 @@ class ShortcutButton implements ButtonInterface, PositionInterface
protected function createShortcutMarkup(): string
{
$moduleName = $this->moduleName;
$storeUrl = HttpUtility::buildQueryString($this->arguments, '&');
$routeIdentifier = $this->routeIdentifier;
$arguments = $this->arguments;
$iconFactory = GeneralUtility::makeInstance(IconFactory::class);
// Find out if this shortcut exists already. Note this is a hack based on the fact
// that sys_be_shortcuts stores the entire request string and not just needed params as array.
$pathInfo = parse_url(GeneralUtility::getIndpEnv('REQUEST_URI'));
$shortcutUrl = $pathInfo['path'] . '?' . $storeUrl;
$shortcutRepository = GeneralUtility::makeInstance(ShortcutRepository::class);
$shortcutExist = $shortcutRepository->shortcutExists($shortcutUrl);
if (strpos($routeIdentifier, '/') !== false) {
trigger_error('Automatic fallback for the route path is deprecated and will be removed in v12.', E_USER_DEPRECATED);
$routeIdentifier = $this->getRouteIdentifierByRoutePath($routeIdentifier);
}
$iconFactory = GeneralUtility::makeInstance(IconFactory::class);
if ($shortcutExist) {
$shortcutMarkup = '<a class="active btn btn-default btn-sm" title="">'
if ($routeIdentifier === '' && $this->moduleName !== '') {
trigger_error('Using ShortcutButton::$moduleNname is deprecated and will be removed in v12. Use ShortcutButton::$routeIdentifier instead.', E_USER_DEPRECATED);
$routeIdentifier = $this->getRouteIdentifierByModuleName($this->moduleName);
}
if (isset($arguments['route'])) {
trigger_error('Using route as an argument is deprecated and will be removed in v12. Set the route identifier with ShortcutButton::setRouteIdentifier() instead.', E_USER_DEPRECATED);
if ($routeIdentifier === '' && is_string($arguments['route'])) {
$routeIdentifier = $this->getRouteIdentifierByRoutePath($arguments['route']);
}
unset($arguments['route']);
}
// No route found so no shortcut button will be rendered
if ($routeIdentifier === '' || !$this->routeExists($routeIdentifier)) {
return '';
}
// returnUrl will not longer be stored in the database
unset($arguments['returnUrl']);
// Encode arguments to be stored in the database
$arguments = json_encode($arguments);
if (GeneralUtility::makeInstance(ShortcutRepository::class)->shortcutExists($routeIdentifier, $arguments)) {
return '<a class="active btn btn-default btn-sm" title="">'
. $iconFactory->getIcon('actions-system-shortcut-active', Icon::SIZE_SMALL)->render()
. '</a>';
} else {
$languageService = $this->getLanguageService();
$confirmationText = $languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.makeBookmark');
}
$confirmationText = $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.makeBookmark');
$onClick = 'top.TYPO3.ShortcutMenu.createShortcut('
. GeneralUtility::quoteJSvalue(rawurlencode($moduleName))
. ', ' . GeneralUtility::quoteJSvalue(rawurlencode($shortcutUrl))
. GeneralUtility::quoteJSvalue($routeIdentifier)
. ', ' . GeneralUtility::quoteJSvalue($arguments)
. ', ' . GeneralUtility::quoteJSvalue($this->displayName)
. ', ' . GeneralUtility::quoteJSvalue($confirmationText)
. ', \'\''
. ', this'
. ', ' . GeneralUtility::quoteJSvalue($this->displayName) . ');return false;';
$shortcutMarkup = '<a href="#" class="btn btn-default btn-sm" onclick="' . htmlspecialchars($onClick) . '" title="'
. htmlspecialchars($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.makeBookmark')) . '">'
. $iconFactory->getIcon('actions-system-shortcut-new', Icon::SIZE_SMALL)->render() . '</a>';
. ', this);return false;';
return '<a href="#" class="btn btn-default btn-sm" onclick="' . htmlspecialchars($onClick) . '" title="' . htmlspecialchars($confirmationText) . '">'
. $iconFactory->getIcon('actions-system-shortcut-new', Icon::SIZE_SMALL)->render()
. '</a>';
}
return $shortcutMarkup;
/**
* Map a given route path to its route identifier
*
* @param string $routePath
* @return string
* @deprecated Only for backwards compatibility. Can be removed in v12.
*/
protected function getRouteIdentifierByRoutePath(string $routePath): string
{
foreach ($this->getRoutes() as $identifier => $route) {
if ($route->getPath() === $routePath
&& (
$route->hasOption('moduleName')
|| in_array($identifier, ['record_edit', 'file_edit', 'wizard_rte'], true)
)
) {
return $identifier;
}
}
return '';
}
/**
* Map a given module name to its route identifier by respecting some special cases
*
* @param string $moduleName
* @return string
* @deprecated Only for backwards compatibility. Can be removed in v12.
*/
protected function getRouteIdentifierByModuleName(string $moduleName): string
{
$identifier = '';
// Special case module names
switch ($moduleName) {
case 'xMOD_alt_doc.php':
$identifier = 'record_edit';
break;
case 'file_edit':
case 'wizard_rte':
$identifier = $moduleName;
break;
}
if ($identifier !== '') {
return $identifier;
}
foreach ($this->getRoutes() as $identifier => $route) {
if ($route->hasOption('moduleName') && $route->getOption('moduleName') === $moduleName) {
return $identifier;
}
}
return '';
}
protected function routeExists(string $routeIdentifier): bool
{
return (bool)($this->getRoutes()[$routeIdentifier] ?? false);
}
protected function getRoutes(): iterable
{
return GeneralUtility::makeInstance(Router::class)->getRoutes();
}
protected function getBackendUser(): BackendUserAuthentication
......
......@@ -16,6 +16,7 @@
namespace TYPO3\CMS\Backend\Template;
use TYPO3\CMS\Backend\Backend\Shortcut\ShortcutRepository;
use TYPO3\CMS\Backend\Routing\Router;
use TYPO3\CMS\Backend\Template\Components\DocHeaderComponent;
use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
......@@ -540,32 +541,28 @@ class ModuleTemplate
if (GeneralUtility::_GET('route') !== null) {
$storeUrl = '&route=' . $moduleName . $storeUrl;
}
if ((int)$motherModName === 1) {
$motherModule = 'top.currentModuleLoaded';
} elseif (is_string($motherModName) && $motherModName !== '') {
$motherModule = GeneralUtility::quoteJSvalue($motherModName);
} else {
$motherModule = '\'\'';
}
$confirmationText = GeneralUtility::quoteJSvalue(
$this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.makeBookmark')
);
$shortcutUrl = $pathInfo['path'] . '?' . $storeUrl;
$shortcutRepository = GeneralUtility::makeInstance(ShortcutRepository::class);
$shortcutExist = $shortcutRepository->shortcutExists($shortcutUrl);
if ($shortcutExist) {
// We simply let the above functionality as it is for maximum backwards compatibility and now
// just process the generated $shortcutUrl to match the new format (routeIdentifier & arguments)
[$routeIdentifier, $arguments] = $this->getCreateShortcutProperties($shortcutUrl);
if (GeneralUtility::makeInstance(ShortcutRepository::class)->shortcutExists($routeIdentifier, $arguments)) {
return '<a class="active ' . htmlspecialchars($classes) . '" title="">' .
$this->iconFactory->getIcon('actions-system-shortcut-active', Icon::SIZE_SMALL)->render() . '</a>';
}
$url = GeneralUtility::quoteJSvalue(rawurlencode($shortcutUrl));
$onClick = 'top.TYPO3.ShortcutMenu.createShortcut(' . GeneralUtility::quoteJSvalue(rawurlencode($modName)) .
', ' . $url . ', ' . $confirmationText . ', ' . $motherModule . ', this, ' . GeneralUtility::quoteJSvalue($displayName) . ');return false;';
$confirmationText = $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.makeBookmark');
$onClick = 'top.TYPO3.ShortcutMenu.createShortcut('
. GeneralUtility::quoteJSvalue($routeIdentifier)
. ', ' . GeneralUtility::quoteJSvalue($arguments)
. ', ' . GeneralUtility::quoteJSvalue($displayName)
. ', ' . GeneralUtility::quoteJSvalue($confirmationText)
. ', this);return false;';
return '<a href="#" class="' . htmlspecialchars($classes) . '" onclick="' . htmlspecialchars($onClick) . '" title="' .
htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.makeBookmark')) . '">' .
htmlspecialchars($confirmationText) . '">' .
$this->iconFactory->getIcon('actions-system-shortcut-new', Icon::SIZE_SMALL)->render() . '</a>';
}
......@@ -592,6 +589,41 @@ class ModuleTemplate
return HttpUtility::buildQueryString($storeArray, '&');
}
/**
* Process the generated shortcut url and return properties needed for the
* shortcut registration with route identifier and JSON encoded arguments.
*
* @param string $shortcutUrl
*
* @return array
* @deprecated Only for backwards compatibility. Can be removed in v12
*/
protected function getCreateShortcutProperties(string $shortcutUrl): array
{
$routeIdentifier = '';
$arguments = [];
parse_str(parse_url($shortcutUrl)['query'] ?? '', $arguments);
$routePath = (string)($arguments['route'] ?? '');
if ($routePath !== '') {
foreach (GeneralUtility::makeInstance(Router::class)->getRoutes() as $identifier => $route) {
if ($route->getPath() === $routePath
&& (
$route->hasOption('moduleName')
|| in_array($identifier, ['record_edit', 'file_edit', 'wizard_rte'], true)
)
) {
$routeIdentifier = $identifier;
}
}
}
unset($arguments['route'], $arguments['returnUrl']);
return [$routeIdentifier, json_encode($arguments)];
}
/**
* Retrieves configured favicon for backend (with fallback)
*
......
......@@ -17,6 +17,7 @@ declare(strict_types=1);
namespace TYPO3\CMS\Backend\ViewHelpers\ModuleLayout\Button;
use TYPO3\CMS\Backend\Routing\Router;
use TYPO3\CMS\Backend\Template\Components\ButtonBar;
use TYPO3\CMS\Backend\Template\Components\Buttons\ButtonInterface;
use TYPO3\CMS\Core\Utility\GeneralUtility;
......@@ -37,7 +38,7 @@ use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
* Default::
*
* <be:moduleLayout>
* <be:moduleLayout.button.shortcutButton displayName="Shortcut label" arguments="{route: '{route}'"/>
* <be:moduleLayout.button.shortcutButton displayName="Shortcut label" arguments="{parameter: '{someValue}'}"/>
* </be:moduleLayout>
*/
class ShortcutButtonViewHelper extends AbstractButtonViewHelper
......@@ -50,7 +51,8 @@ class ShortcutButtonViewHelper extends AbstractButtonViewHelper
public function initializeArguments(): void
{
parent::initializeArguments();
$this->registerArgument('displayName', 'string', 'Name for the shortcut');
// This will be required in v12. Deprecation for empty argument logged by ModuleTemplate->makeShortcutIcon()
$this->registerArgument('displayName', 'string', 'Name for the shortcut', false, '');
$this->registerArgument('arguments', 'array', 'List of relevant GET variables as key/values list to store', false, []);
// @deprecated since v11, will be removed in v12. Use 'arguments' instead. Deprecation logged by ModuleTemplate->makeShortcutIcon()
$this->registerArgument('getVars', 'array', 'List of additional GET variables to store. The current id, module and all module arguments will always be stored', false, []);
......@@ -62,9 +64,11 @@ class ShortcutButtonViewHelper extends AbstractButtonViewHelper
$moduleName = $currentRequest->getPluginName();
$displayName = $arguments['displayName'];
$shortcutButton = $buttonBar->makeShortcutButton()
// Initialize the shortcut button
$shortcutButton = $buttonBar
->makeShortcutButton()
->setDisplayName($displayName)
->setModuleName($moduleName);
->setRouteIdentifier(self::getRouteIdentifierForModuleName($moduleName));
if (!empty($arguments['arguments'])) {
$shortcutButton->setArguments($arguments['arguments']);
......@@ -81,4 +85,21 @@ class ShortcutButtonViewHelper extends AbstractButtonViewHelper
return $shortcutButton;
}
/**
* Tries to fetch the route identifier for a given module name
*
* @param string $moduleName
* @return string
*/
protected static function getRouteIdentifierForModuleName(string $moduleName): string
{
foreach (GeneralUtility::makeInstance(Router::class)->getRoutes() as $identifier => $route) {
if ($route->hasOption('moduleName') && $route->getOption('moduleName') === $moduleName) {
return $identifier;
}
}
return '';
}
}
......@@ -38,6 +38,9 @@ services:
TYPO3\CMS\Backend\History\RecordHistoryRollback:
public: true
TYPO3\CMS\Backend\Backend\Shortcut\ShortcutRepository:
public: true
TYPO3\CMS\Backend\Controller\AboutController:
tags: ['backend.controller']
......
......@@ -10,4 +10,4 @@
*
* The TYPO3 project - inspiring people to share!
*/
var __importDefault=this&&this.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};define(["require","exports","jquery","TYPO3/CMS/Core/Ajax/AjaxRequest","../Icons","../Modal","../Notification","../Viewport"],(function(t,e,o,r,c,a,s,l){"use strict";var n;o=__importDefault(o),function(t){t.containerSelector="#typo3-cms-backend-backend-toolbaritems-shortcuttoolbaritem",t.toolbarIconSelector=".dropdown-toggle span.icon",t.toolbarMenuSelector=".dropdown-menu",t.shortcutItemSelector=".t3js-topbar-shortcut",t.shortcutDeleteSelector=".t3js-shortcut-delete",t.shortcutEditSelector=".t3js-shortcut-edit",t.shortcutFormTitleSelector='input[name="shortcut-title"]',t.shortcutFormGroupSelector='select[name="shortcut-group"]',t.shortcutFormSaveSelector=".shortcut-form-save",t.shortcutFormCancelSelector=".shortcut-form-cancel",t.shortcutFormSelector=".shortcut-form"}(n||(n={}));let u=new class{constructor(){this.initializeEvents=()=>{o.default(n.containerSelector).on("click",n.shortcutDeleteSelector,t=>{t.preventDefault(),t.stopImmediatePropagation(),this.deleteShortcut(o.default(t.currentTarget).closest(n.shortcutItemSelector))}).on("click",n.shortcutFormGroupSelector,t=>{t.preventDefault(),t.stopImmediatePropagation()}).on("click",n.shortcutEditSelector,t=>{t.preventDefault(),t.stopImmediatePropagation(),this.editShortcut(o.default(t.currentTarget).closest(n.shortcutItemSelector))}).on("click",n.shortcutFormSaveSelector,t=>{t.preventDefault(),t.stopImmediatePropagation(),this.saveShortcutForm(o.default(t.currentTarget).closest(n.shortcutFormSelector))}).on("submit",n.shortcutFormSelector,t=>{t.preventDefault(),t.stopImmediatePropagation(),this.saveShortcutForm(o.default(t.currentTarget).closest(n.shortcutFormSelector))}).on("click",n.shortcutFormCancelSelector,t=>{t.preventDefault(),t.stopImmediatePropagation(),this.refreshMenu()})},l.Topbar.Toolbar.registerEvent(this.initializeEvents)}createShortcut(t,e,s,l,u,i){void 0!==s&&a.confirm(TYPO3.lang["bookmark.create"],s).on("confirm.button.ok",a=>{const s=o.default(n.toolbarIconSelector,n.containerSelector),h=s.clone();c.getIcon("spinner-circle-light",c.sizes.small).then(t=>{s.replaceWith(t)}),new r(TYPO3.settings.ajaxUrls.shortcut_create).post({module:t,url:e,motherModName:l,displayName:i}).then(()=>{this.refreshMenu(),o.default(n.toolbarIconSelector,n.containerSelector).replaceWith(h),"object"==typeof u&&(c.getIcon("actions-system-shortcut-active",c.sizes.small).then(t=>{o.default(u).html(t)}),o.default(u).addClass("active"),o.default(u).attr("title",null),o.default(u).attr("onclick",null))}),o.default(a.currentTarget).trigger("modal-dismiss")}).on("confirm.button.cancel",t=>{o.default(t.currentTarget).trigger("modal-dismiss")})}deleteShortcut(t){a.confirm(TYPO3.lang["bookmark.delete"],TYPO3.lang["bookmark.confirmDelete"]).on("confirm.button.ok",e=>{new r(TYPO3.settings.ajaxUrls.shortcut_remove).post({shortcutId:t.data("shortcutid")}).then(()=>{this.refreshMenu()}),o.default(e.currentTarget).trigger("modal-dismiss")}).on("confirm.button.cancel",t=>{o.default(t.currentTarget).trigger("modal-dismiss")})}editShortcut(t){new r(TYPO3.settings.ajaxUrls.shortcut_editform).withQueryArguments({shortcutId:t.data("shortcutid"),shortcutGroup:t.data("shortcutgroup")}).get({cache:"no-cache"}).then(async t=>{o.default(n.containerSelector).find(n.toolbarMenuSelector).html(await t.resolve())})}saveShortcutForm(t){new r(TYPO3.settings.ajaxUrls.shortcut_saveform).post({shortcutId:t.data("shortcutid"),shortcutTitle:t.find(n.shortcutFormTitleSelector).val(),shortcutGroup:t.find(n.shortcutFormGroupSelector).val()}).then(()=>{s.success(TYPO3.lang["bookmark.savedTitle"],TYPO3.lang["bookmark.savedMessage"]),this.refreshMenu()})}refreshMenu(){new r(TYPO3.settings.ajaxUrls.shortcut_list).get({cache:"no-cache"}).then(async t=>{o.default(n.toolbarMenuSelector,n.containerSelector).html(await t.resolve())})}};return TYPO3.ShortcutMenu=u,u}));
\ No newline at end of file
var __importDefault=this&&this.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};define(["require","exports","jquery","TYPO3/CMS/Core/Ajax/AjaxRequest","../Icons","../Modal","../Notification","../Viewport"],(function(t,e,o,r,c,a,s,l){"use strict";var n;o=__importDefault(o),function(t){t.containerSelector="#typo3-cms-backend-backend-toolbaritems-shortcuttoolbaritem",t.toolbarIconSelector=".dropdown-toggle span.icon",t.toolbarMenuSelector=".dropdown-menu",t.shortcutItemSelector=".t3js-topbar-shortcut",t.shortcutDeleteSelector=".t3js-shortcut-delete",t.shortcutEditSelector=".t3js-shortcut-edit",t.shortcutFormTitleSelector='input[name="shortcut-title"]',t.shortcutFormGroupSelector='select[name="shortcut-group"]',t.shortcutFormSaveSelector=".shortcut-form-save",t.shortcutFormCancelSelector=".shortcut-form-cancel",t.shortcutFormSelector=".shortcut-form"}(n||(n={}));let u=new class{