[TASK] Unsplit the splitbutton 93/56693/36
authorPatrick Broens <patrick@patrickbroens.nl>
Mon, 16 Apr 2018 18:27:45 +0000 (20:27 +0200)
committerSusanne Moog <susanne.moog@typo3.org>
Fri, 22 Jun 2018 16:09:23 +0000 (18:09 +0200)
This patch splits the splitbutton into several separate buttons, which
all serve a single action. Modals are used for guiding the user
to prevent possible data loss based on their actions.

Concept: Rachel Foucard
Proofread: Tom Warwick
Resolves: #84753
Releases: master
Change-Id: I58bfe7621b1bc03ef9222bca740dbbe050c07fc9
Reviewed-on: https://review.typo3.org/56693
Tested-by: TYPO3com <no-reply@typo3.com>
Reviewed-by: Frank Naegler <frank.naegler@typo3.org>
Tested-by: Frank Naegler <frank.naegler@typo3.org>
Reviewed-by: Susanne Moog <susanne.moog@typo3.org>
Tested-by: Susanne Moog <susanne.moog@typo3.org>
typo3/sysext/backend/Classes/Controller/EditDocumentController.php
typo3/sysext/backend/Classes/Template/Components/Buttons/AbstractButton.php
typo3/sysext/backend/Classes/Template/Components/Buttons/InputButton.php
typo3/sysext/backend/Classes/Utility/BackendUtility.php
typo3/sysext/backend/Resources/Private/Language/locallang_alt_doc.xlf
typo3/sysext/backend/Resources/Public/JavaScript/FormEngine.js
typo3/sysext/backend/Resources/Public/JavaScript/jsfunc.inline.js
typo3/sysext/core/Resources/Private/Language/locallang_core.xlf
typo3/sysext/core/Tests/Acceptance/Backend/Formhandler/ElementsGroupCest.php
typo3/sysext/core/Tests/Acceptance/Backend/Template/TemplateCest.php

index 88b5e5b..f34e74a 100644 (file)
@@ -34,6 +34,7 @@ use TYPO3\CMS\Core\Database\Query\QueryBuilder;
 use TYPO3\CMS\Core\Database\Query\Restriction\BackendWorkspaceRestriction;
 use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
 use TYPO3\CMS\Core\Database\Query\Restriction\HiddenRestriction;
+use TYPO3\CMS\Core\Database\ReferenceIndex;
 use TYPO3\CMS\Core\DataHandling\DataHandler;
 use TYPO3\CMS\Core\Http\HtmlResponse;
 use TYPO3\CMS\Core\Http\RedirectResponse;
@@ -464,6 +465,20 @@ class EditDocumentController
     protected $moduleTemplate;
 
     /**
+     * Check if a record has been saved
+     *
+     * @var bool
+     */
+    protected $isSavedRecord;
+
+    /**
+     * Check if a page in free translation mode
+     *
+     * @var bool
+     */
+    protected $isPageInFreeTranslationMode = false;
+
+    /**
      * Constructor
      */
     public function __construct()
@@ -944,53 +959,57 @@ class EditDocumentController
     }
 
     /**
+     * Generate the Javascript for opening the preview window
+     *
      * @return string
      */
     protected function generatePreviewCode(): string
     {
-        $table = $this->previewData['table'];
-        $recordId = $this->previewData['id'];
+        $previewPageId = $this->getPreviewPageId();
+        $previewPageRootLine = BackendUtility::BEgetRootLine($previewPageId);
+        $anchorSection = $this->getPreviewUrlAnchorSection();
+        $previewUrlParameters = $this->getPreviewUrlParameters($previewPageId);
 
-        if ($table === 'pages') {
-            $currentPageId = $recordId;
-        } else {
-            $currentPageId = MathUtility::convertToPositiveInteger($this->popViewId);
-        }
-
-        $previewConfiguration = BackendUtility::getPagesTSconfig($currentPageId)['TCEMAIN.']['preview.'][$table . '.'] ?? [];
-
-        $recordArray = BackendUtility::getRecord($table, $recordId);
-
-        // find the right preview page id
-        $previewPageId = 0;
-        if (isset($previewConfiguration['previewPageId'])) {
-            $previewPageId = $previewConfiguration['previewPageId'];
-        }
-        // if no preview page was configured
-        if (!$previewPageId) {
-            $rootPageData = null;
-            $rootLine = BackendUtility::BEgetRootLine($currentPageId);
-            $currentPage = reset($rootLine);
-            // Allow all doktypes below 200
-            // This makes custom doktype work as well with opening a frontend page.
-            if ((int)$currentPage['doktype'] <= PageRepository::DOKTYPE_SPACER) {
-                // try the current page
-                $previewPageId = $currentPageId;
+        return '
+            if (window.opener) {
+                '
+                . BackendUtility::viewOnClick(
+                    $previewPageId,
+                    '',
+                    $previewPageRootLine,
+                    $anchorSection,
+                    $this->viewUrl,
+                    $previewUrlParameters,
+                    false
+                )
+            . '
             } else {
-                // or search for the root page
-                foreach ($rootLine as $page) {
-                    if ($page['is_siteroot']) {
-                        $rootPageData = $page;
-                        break;
-                    }
-                }
-                $previewPageId = isset($rootPageData)
-                    ? (int)$rootPageData['uid']
-                    : $currentPageId;
-            }
-        }
+            '
+                . BackendUtility::viewOnClick(
+                    $previewPageId,
+                    '',
+                    $previewPageRootLine,
+                    $anchorSection,
+                    $this->viewUrl,
+                    $previewUrlParameters
+                )
+            . '
+            }';
+    }
 
+    /**
+     * Returns the parameters for the preview URL
+     *
+     * @param int $previewPageId
+     * @return string
+     */
+    protected function getPreviewUrlParameters(int $previewPageId): string
+    {
         $linkParameters = [];
+        $table = $this->previewData['table'] ?: $this->firstEl['table'];
+        $recordId = $this->previewData['id'] ?: $this->firstEl['uid'];
+        $previewConfiguration = $pageTsConfig['TCEMAIN.']['preview.'][$table . '.'] ?? [];
+        $recordArray = BackendUtility::getRecord($table, $recordId);
 
         // language handling
         $languageField = $GLOBALS['TCA'][$table]['ctrl']['languageField'] ?? '';
@@ -1036,36 +1055,72 @@ class EditDocumentController
             $linkParameters['no_cache'] = 1;
         }
 
-        $this->popViewId = $previewPageId;
-        $popViewId_addParams = GeneralUtility::implodeArrayForUrl('', $linkParameters, '', false, true);
-        $anchorSection = $table === 'tt_content' ? '#c' . $recordId : '';
+        return GeneralUtility::implodeArrayForUrl('', $linkParameters, '', false, true);
+    }
 
-        $previewPageRootLine = BackendUtility::BEgetRootLine($this->popViewId);
-        return '
-            if (window.opener) {
-                '
-                . BackendUtility::viewOnClick(
-                    $this->popViewId,
-                    '',
-                    $previewPageRootLine,
-                    $anchorSection,
-                    $this->viewUrl,
-                    $popViewId_addParams,
-                    false
-                )
-            . '
+    /**
+     * Returns the anchor section for the preview url
+     *
+     * @return string
+     */
+    protected function getPreviewUrlAnchorSection(): string
+    {
+        $table = $this->previewData['table'] ?: $this->firstEl['table'];
+        $recordId = $this->previewData['id'] ?: $this->firstEl['uid'];
+
+        return $table === 'tt_content' ? '#c' . (int)$recordId : '';
+    }
+
+    /**
+     * Returns the preview page id
+     *
+     * @return int
+     */
+    protected function getPreviewPageId(): int
+    {
+        $previewPageId = 0;
+        $table = $this->previewData['table'] ?: $this->firstEl['table'];
+        $recordId = $this->previewData['id'] ?: $this->firstEl['uid'];
+        $pageId = $this->popViewId ?: $this->viewId;
+
+        if ($table === 'pages') {
+            $currentPageId = (int)$recordId;
+        } else {
+            $currentPageId = MathUtility::convertToPositiveInteger($pageId);
+        }
+
+        $previewConfiguration = $pageTsConfig['TCEMAIN.']['preview.'][$table . '.'] ?? [];
+
+        if (isset($previewConfiguration['previewPageId'])) {
+            $previewPageId = $previewConfiguration['previewPageId'];
+        }
+        // if no preview page was configured
+        if (!$previewPageId) {
+            $rootPageData = null;
+            $rootLine = BackendUtility::BEgetRootLine($currentPageId);
+            $currentPage = reset($rootLine);
+            // Allow all doktypes below 200
+            // This makes custom doktype work as well with opening a frontend page.
+            if ((int)$currentPage['doktype'] <= PageRepository::DOKTYPE_SPACER) {
+                // try the current page
+                $previewPageId = $currentPageId;
             } else {
-            '
-                . BackendUtility::viewOnClick(
-                    $this->popViewId,
-                    '',
-                    $previewPageRootLine,
-                    $anchorSection,
-                    $this->viewUrl,
-                    $popViewId_addParams
-                )
-            . '
-            }';
+                // or search for the root page
+                foreach ($rootLine as $page) {
+                    if ($page['is_siteroot']) {
+                        $rootPageData = $page;
+                        break;
+                    }
+                }
+                $previewPageId = isset($rootPageData)
+                    ? (int)$rootPageData['uid']
+                    : $currentPageId;
+            }
+        }
+
+        $this->popViewId = $previewPageId;
+
+        return $previewPageId;
     }
 
     /**
@@ -1230,7 +1285,10 @@ class EditDocumentController
 
                                 // Determine if delete button can be shown
                                 $deleteAccess = false;
-                                if ($command === 'edit') {
+                                if (
+                                    $command === 'edit'
+                                    || $command === 'new'
+                                ) {
                                     $permission = $formData['userPermissionOnPage'];
                                     if ($formData['tableName'] === 'pages') {
                                         $deleteAccess = $permission & Permission::PAGE_DELETE ? true : false;
@@ -1337,299 +1395,531 @@ class EditDocumentController
      */
     protected function getButtons(ServerRequestInterface $request): void
     {
-        $lang = $this->getLanguageService();
-        /** @var UriBuilder $uriBuilder */
-        $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
-
-        $isSavedRecord = (
-            $this->firstEl['cmd'] !== 'new'
-            && MathUtility::canBeInterpretedAsInteger($this->firstEl['uid'])
-        );
-
         $record = BackendUtility::getRecord($this->firstEl['table'], $this->firstEl['uid']);
         $TCActrl = $GLOBALS['TCA'][$this->firstEl['table']]['ctrl'];
 
+        $this->setIsSavedRecord();
+
         $sysLanguageUid = 0;
-        if ($isSavedRecord
-            && isset($TCActrl['languageField'], $record[$TCActrl['languageField']])) {
+        if (
+            $this->isSavedRecord
+            && isset($TCActrl['languageField'], $record[$TCActrl['languageField']])
+        ) {
             $sysLanguageUid = (int)$record[$TCActrl['languageField']];
         } elseif (isset($this->defVals['sys_language_uid'])) {
             $sysLanguageUid = (int)$this->defVals['sys_language_uid'];
         }
 
-        // Render SAVE type buttons:
-        // The action of each button is decided by its name attribute.
+        $l18nParent = isset($TCActrl['transOrigPointerField'], $record[$TCActrl['transOrigPointerField']])
+            ? (int)$record[$TCActrl['transOrigPointerField']]
+            : 0;
+
+        $this->setIsPageInFreeTranslationMode($record, $sysLanguageUid);
+
         $buttonBar = $this->moduleTemplate->getDocHeaderComponent()->getButtonBar();
-        if (!$this->errorC && !$GLOBALS['TCA'][$this->firstEl['table']]['ctrl']['readOnly']) {
-            $saveSplitButton = $buttonBar->makeSplitButton();
-            // SAVE button:
-            $saveButton = $buttonBar->makeInputButton()
-                ->setTitle($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:rm.saveDoc'))
-                ->setName('_savedok')
-                ->setValue('1')
-                ->setForm('EditDocumentController')
-                ->setIcon($this->moduleTemplate->getIconFactory()->getIcon('actions-document-save', Icon::SIZE_SMALL));
-            $saveSplitButton->addItem($saveButton, true);
-
-            // SAVE / VIEW button:
-            if ($this->viewId && !$this->noView && !empty($this->firstEl['table']) && $this->getTsConfigOption($this->firstEl['table'], 'saveDocView')) {
-                $pagesTSconfig = BackendUtility::getPagesTSconfig($this->pageinfo['uid']);
-                if (isset($pagesTSconfig['TCEMAIN.']['preview.']['disableButtonForDokType'])) {
-                    $excludeDokTypes = GeneralUtility::intExplode(
-                        ',',
-                        $pagesTSconfig['TCEMAIN.']['preview.']['disableButtonForDokType'],
-                        true
-                    );
-                } else {
-                    // exclude sysfolders, spacers and recycler by default
-                    $excludeDokTypes = [
-                        PageRepository::DOKTYPE_RECYCLER,
-                        PageRepository::DOKTYPE_SYSFOLDER,
-                        PageRepository::DOKTYPE_SPACER
-                    ];
-                }
-                if (!in_array((int)$this->pageinfo['doktype'], $excludeDokTypes, true)
-                    || isset($pagesTSconfig['TCEMAIN.']['preview.'][$this->firstEl['table'] . '.']['previewPageId'])
-                ) {
-                    $saveAndOpenButton = $buttonBar->makeInputButton()
-                        ->setTitle($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:rm.saveDocShow'))
-                        ->setName('_savedokview')
-                        ->setValue('1')
-                        ->setForm('EditDocumentController')
-                        ->setOnClick("window.open('', 'newTYPO3frontendWindow');")
-                        ->setIcon($this->moduleTemplate->getIconFactory()->getIcon(
-                            'actions-document-save-view',
-                            Icon::SIZE_SMALL
-                        ));
-                    $saveSplitButton->addItem($saveAndOpenButton);
-                }
-            }
 
-            // SAVE / NEW button:
-            $showSaveAndNewButton = (
-                count($this->elementsData) === 1
-                && !empty($this->firstEl['table'])
-                && $this->getTsConfigOption($this->firstEl['table'], 'saveDocNew')
+        $this->registerCloseButtonToButtonBar($buttonBar, ButtonBar::BUTTON_POSITION_LEFT, 1);
+
+        // Show buttons when table is not read-only
+        if (
+            !$this->errorC
+            && !$GLOBALS['TCA'][$this->firstEl['table']]['ctrl']['readOnly']
+        ) {
+            $this->registerSaveButtonToButtonBar($buttonBar, ButtonBar::BUTTON_POSITION_LEFT, 2);
+            $this->registerViewButtonToButtonBar($buttonBar, ButtonBar::BUTTON_POSITION_LEFT, 3);
+            $this->registerNewButtonToButtonBar(
+                $buttonBar,
+                ButtonBar::BUTTON_POSITION_LEFT,
+                4,
+                $sysLanguageUid,
+                $l18nParent
+            );
+            $this->registerDuplicationButtonToButtonBar(
+                $buttonBar,
+                ButtonBar::BUTTON_POSITION_LEFT,
+                5,
+                $sysLanguageUid,
+                $l18nParent
+            );
+            $this->registerDeleteButtonToButtonBar($buttonBar, ButtonBar::BUTTON_POSITION_LEFT, 6);
+            $this->registerColumnsOnlyButtonToButtonBar($buttonBar, ButtonBar::BUTTON_POSITION_LEFT, 7);
+            $this->registerHistoryButtonToButtonBar($buttonBar, ButtonBar::BUTTON_POSITION_RIGHT, 1);
+        }
+
+        $this->registerOpenInNewWindowButtonToButtonBar($buttonBar, ButtonBar::BUTTON_POSITION_RIGHT, 2);
+        $this->registerShortcutButtonToButtonBar($buttonBar, ButtonBar::BUTTON_POSITION_RIGHT, 3);
+        $this->registerCshButtonToButtonBar($buttonBar, ButtonBar::BUTTON_POSITION_RIGHT, 4);
+    }
+
+    /**
+     * Set the boolean to check if the record is saved
+     */
+    protected function setIsSavedRecord()
+    {
+        if (!is_bool($this->isSavedRecord)) {
+            $this->isSavedRecord = (
+                $this->firstEl['cmd'] !== 'new'
+                && MathUtility::canBeInterpretedAsInteger($this->firstEl['uid'])
             );
-            // Hide the button for tt_content when in connected translation mode
-            if ($this->firstEl['table'] === 'tt_content') {
-                $showSaveAndNewButton = $this->isPageInFreeTranslationMode(
+        }
+    }
+
+    /**
+     * Returns if inconsistent language handling is allowed
+     *
+     * @return bool
+     */
+    protected function isInconsistentLanguageHandlingAllowed(): bool
+    {
+        $allowInconsistentLanguageHandling = BackendUtility::getPagesTSconfig(
+            $this->pageinfo['uid']
+        )['mod']['web_layout']['allowInconsistentLanguageHandling'];
+
+        return $allowInconsistentLanguageHandling['value'] === '1';
+    }
+
+    /**
+     * Set the boolean to check if the page is in free translation mode
+     *
+     * @param array|null $record
+     * @param int $sysLanguageUid
+     */
+    protected function setIsPageInFreeTranslationMode($record, int $sysLanguageUid)
+    {
+        if ($this->firstEl['table'] === 'tt_content') {
+            if (!$this->isSavedRecord) {
+                $this->isPageInFreeTranslationMode = $this->getFreeTranslationMode(
                     (int)$this->pageinfo['uid'],
-                    !$isSavedRecord ? (int)$this->defVals['colPos'] : (int)$record['colPos'],
+                    (int)$this->defVals['colPos'],
+                    $sysLanguageUid
+                );
+            } else {
+                $this->isPageInFreeTranslationMode = $this->getFreeTranslationMode(
+                    (int)$this->pageinfo['uid'],
+                    (int)$record['colPos'],
                     $sysLanguageUid
                 );
             }
-            if ($showSaveAndNewButton) {
-                $saveAndNewButton = $buttonBar->makeInputButton()
-                    ->setName('_savedoknew')
-                    ->setClasses('t3js-editform-submitButton')
-                    ->setValue('1')
-                    ->setForm('EditDocumentController')
-                    ->setTitle($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:rm.saveNewDoc'))
-                    ->setIcon($this->moduleTemplate->getIconFactory()->getIcon(
-                        'actions-document-save-new',
-                        Icon::SIZE_SMALL
-                    ));
-                $saveSplitButton->addItem($saveAndNewButton);
-            }
-            // SAVE / CLOSE
-            $saveAndCloseButton = $buttonBar->makeInputButton()
-                ->setName('_saveandclosedok')
-                ->setClasses('t3js-editform-submitButton')
-                ->setValue('1')
-                ->setForm('EditDocumentController')
-                ->setTitle($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:rm.saveCloseDoc'))
-                ->setIcon($this->moduleTemplate->getIconFactory()->getIcon(
-                    'actions-document-save-close',
-                    Icon::SIZE_SMALL
-                ));
-            $saveSplitButton->addItem($saveAndCloseButton);
-            $buttonBar->addButton($saveSplitButton, ButtonBar::BUTTON_POSITION_LEFT, 2);
         }
-        // CLOSE button:
+    }
+
+    /**
+     * Check if the page is in free translation mode
+     *
+     * @param int $page
+     * @param int $column
+     * @param int $language
+     * @return bool
+     */
+    protected function getFreeTranslationMode(int $page, int $column, int $language): bool
+    {
+        $freeTranslationMode = false;
+
+        if (
+            $this->getConnectedContentElementTranslationsCount($page, $column, $language) === 0
+            && $this->getStandAloneContentElementTranslationsCount($page, $column, $language) >= 0
+        ) {
+            $freeTranslationMode = true;
+        }
+
+        return $freeTranslationMode;
+    }
+
+    /**
+     * Register the close button to the button bar
+     *
+     * @param ButtonBar $buttonBar
+     * @param string $position
+     * @param int $group
+     */
+    protected function registerCloseButtonToButtonBar(ButtonBar $buttonBar, string $position, int $group)
+    {
         $closeButton = $buttonBar->makeLinkButton()
             ->setHref('#')
             ->setClasses('t3js-editform-close')
-            ->setTitle($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:rm.closeDoc'))
+            ->setTitle($this->getLanguageService()->sL(
+                'LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:rm.closeDoc'
+            ))
+            ->setShowLabelText(true)
             ->setIcon($this->moduleTemplate->getIconFactory()->getIcon(
                 'actions-close',
                 Icon::SIZE_SMALL
             ));
-        $buttonBar->addButton($closeButton);
-        // DUPLICATE button:
-        $l18nParent = isset($TCActrl['transOrigPointerField'], $record[$TCActrl['transOrigPointerField']])
-            ? (int)$record[$TCActrl['transOrigPointerField']]
-            : 0;
-        $showDuplicateButton = false;
+
+        $buttonBar->addButton($closeButton, $position, $group);
+    }
+
+    /**
+     * Register the save button to the button bar
+     *
+     * @param ButtonBar $buttonBar
+     * @param string $position
+     * @param int $group
+     */
+    protected function registerSaveButtonToButtonBar(ButtonBar $buttonBar, string $position, int $group)
+    {
+        $saveButton = $buttonBar->makeInputButton()
+            ->setForm('EditDocumentController')
+            ->setIcon($this->moduleTemplate->getIconFactory()->getIcon('actions-document-save', Icon::SIZE_SMALL))
+            ->setName('_savedok')
+            ->setShowLabelText(true)
+            ->setTitle($this->getLanguageService()->sL(
+                'LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:rm.saveDoc'
+            ))
+            ->setValue('1');
+
+        $buttonBar->addButton($saveButton, $position, $group);
+    }
+
+    /**
+     * Register the view button to the button bar
+     *
+     * @param ButtonBar $buttonBar
+     * @param string $position
+     * @param int $group
+     */
+    protected function registerViewButtonToButtonBar(ButtonBar $buttonBar, string $position, int $group)
+    {
         if (
-            $this->firstEl['cmd'] !== 'new'
-            && $this->firstEl['table'] !== 'sys_file_metadata'
-            && MathUtility::canBeInterpretedAsInteger($this->firstEl['uid'])
-            && !empty($this->firstEl['table'])
-            && $this->getTsConfigOption($this->firstEl['table'], 'showDuplicate')
+            $this->viewId // Pid to show the record
+            && !$this->noView // Passed parameter
+            && !empty($this->firstEl['table']) // No table
+
+            // @TODO: TsConfig option should change to viewDoc
+            && $this->getTsConfigOption($this->firstEl['table'], 'saveDocView')
         ) {
-            if ($sysLanguageUid === 0) {
-                // show button, if record is in default language
-                $showDuplicateButton = true;
+            $classNames = 't3js-editform-view';
+
+            $pagesTSconfig = BackendUtility::getPagesTSconfig($this->pageinfo['uid']);
+
+            if (isset($pagesTSconfig['TCEMAIN.']['preview.']['disableButtonForDokType'])) {
+                $excludeDokTypes = GeneralUtility::intExplode(
+                    ',',
+                    $pagesTSconfig['TCEMAIN.']['preview.']['disableButtonForDokType'],
+                    true
+                );
             } else {
-                // show button, if record is NOT in default language AND has no parent
-                $showDuplicateButton = $l18nParent === 0;
+                // exclude sysfolders, spacers and recycler by default
+                $excludeDokTypes = [
+                    PageRepository::DOKTYPE_RECYCLER,
+                    PageRepository::DOKTYPE_SYSFOLDER,
+                    PageRepository::DOKTYPE_SPACER
+                ];
+            }
+
+            if (
+                !in_array((int)$this->pageinfo['doktype'], $excludeDokTypes, true)
+                || isset($pagesTSconfig['TCEMAIN.']['preview.'][$this->firstEl['table'] . '.']['previewPageId'])
+            ) {
+                $previewPageId = $this->getPreviewPageId();
+                $previewUrl = BackendUtility::getPreviewUrl(
+                    $previewPageId,
+                    '',
+                    BackendUtility::BEgetRootLine($previewPageId),
+                    $this->getPreviewUrlAnchorSection(),
+                    $this->viewUrl,
+                    $this->getPreviewUrlParameters($previewPageId)
+                );
+
+                $viewButton = $buttonBar->makeLinkButton()
+                    ->setHref($previewUrl)
+                    ->setIcon($this->moduleTemplate->getIconFactory()->getIcon(
+                        'actions-view',
+                        Icon::SIZE_SMALL
+                    ))
+                    ->setShowLabelText(true)
+                    ->setTitle($this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:rm.viewDoc'));
+
+                if (!$this->isSavedRecord) {
+                    if ($this->firstEl['table'] === 'pages') {
+                        $viewButton->setDataAttributes(['is-new' => '']);
+                    }
+                }
+
+                if ($classNames !== '') {
+                    $viewButton->setClasses($classNames);
+                }
+
+                $buttonBar->addButton($viewButton, $position, $group);
+            }
+        }
+    }
+
+    /**
+     * Register the new button to the button bar
+     *
+     * @param ButtonBar $buttonBar
+     * @param string $position
+     * @param int $group
+     * @param int $sysLanguageUid
+     * @param int $l18nParent
+     */
+    protected function registerNewButtonToButtonBar(
+        ButtonBar $buttonBar,
+        string $position,
+        int $group,
+        int $sysLanguageUid,
+        int $l18nParent
+    ) {
+        if (
+            $this->firstEl['table'] !== 'sys_file_metadata'
+            && !empty($this->firstEl['table'])
+            && (
+                (
+                    (
+                        $this->isInconsistentLanguageHandlingAllowed()
+                        || $this->isPageInFreeTranslationMode
+                    )
+                    && $this->firstEl['table'] === 'tt_content'
+                )
+                || (
+                    $this->firstEl['table'] !== 'tt_content'
+                    && (
+                        $sysLanguageUid === 0
+                        || $l18nParent === 0
+                    )
+                )
+            )
+        ) {
+            $classNames = 't3js-editform-new';
+
+            $newButton = $buttonBar->makeLinkButton()
+                ->setHref('#')
+                ->setIcon($this->moduleTemplate->getIconFactory()->getIcon(
+                    'actions-add',
+                    Icon::SIZE_SMALL
+                ))
+                ->setShowLabelText(true)
+                ->setTitle($this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:rm.newDoc'));
+
+            if (!$this->isSavedRecord) {
+                $newButton->setDataAttributes(['is-new' => '']);
             }
+
+            if ($classNames !== '') {
+                $newButton->setClasses($classNames);
+            }
+
+            $buttonBar->addButton($newButton, $position, $group);
         }
-        if ($showDuplicateButton) {
+    }
+
+    /**
+     * Register the duplication button to the button bar
+     *
+     * @param ButtonBar $buttonBar
+     * @param string $position
+     * @param int $group
+     * @param int $sysLanguageUid
+     * @param int $l18nParent
+     */
+    protected function registerDuplicationButtonToButtonBar(
+        ButtonBar $buttonBar,
+        string $position,
+        int $group,
+        int $sysLanguageUid,
+        int $l18nParent
+    ) {
+        if (
+            $this->firstEl['table'] !== 'sys_file_metadata'
+            && !empty($this->firstEl['table'])
+            && (
+                (
+                    (
+                        $this->isInconsistentLanguageHandlingAllowed()
+                        || $this->isPageInFreeTranslationMode
+                    )
+                    && $this->firstEl['table'] === 'tt_content'
+                )
+                || (
+                    $this->firstEl['table'] !== 'tt_content'
+                    && (
+                        $sysLanguageUid === 0
+                        || $l18nParent === 0
+                    )
+                )
+            )
+            && $this->getTsConfigOption($this->firstEl['table'], 'showDuplicate')
+        ) {
+            $classNames = 't3js-editform-duplicate';
+
             $duplicateButton = $buttonBar->makeLinkButton()
                 ->setHref('#')
-                ->setClasses('t3js-editform-duplicate')
                 ->setShowLabelText(true)
-                ->setTitle($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:rm.duplicateDoc'))
+                ->setTitle($this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:rm.duplicateDoc'))
                 ->setIcon($this->moduleTemplate->getIconFactory()->getIcon(
                     'actions-document-duplicates-select',
                     Icon::SIZE_SMALL
                 ));
-            $buttonBar->addButton($duplicateButton, ButtonBar::BUTTON_POSITION_LEFT, 3);
+
+            if (!$this->isSavedRecord) {
+                $duplicateButton->setDataAttributes(['is-new' => '']);
+            }
+
+            if ($classNames !== '') {
+                $duplicateButton->setClasses($classNames);
+            }
+
+            $buttonBar->addButton($duplicateButton, $position, $group);
         }
-        // DELETE + UNDO buttons:
-        if (!$this->errorC
-            && !$GLOBALS['TCA'][$this->firstEl['table']]['ctrl']['readOnly']
+    }
+
+    /**
+     * Register the delete button to the button bar
+     *
+     * @param ButtonBar $buttonBar
+     * @param string $position
+     * @param int $group
+     */
+    protected function registerDeleteButtonToButtonBar(ButtonBar $buttonBar, string $position, int $group)
+    {
+        if (
+            $this->firstEl['deleteAccess']
+            && !$this->getDisableDelete()
+            && $this->isSavedRecord
             && count($this->elementsData) === 1
         ) {
-            if ($this->firstEl['cmd'] !== 'new' && MathUtility::canBeInterpretedAsInteger($this->firstEl['uid'])) {
-                // Delete:
-                if ($this->firstEl['deleteAccess']
-                    && !$GLOBALS['TCA'][$this->firstEl['table']]['ctrl']['readOnly']
-                    && !$this->getDisableDelete()
+            $classNames = 't3js-editform-delete-record';
+
+            $returnUrl = $this->retUrl;
+            if ($this->firstEl['table'] === 'pages') {
+                parse_str((string)parse_url($returnUrl, PHP_URL_QUERY), $queryParams);
+                if (
+                    isset($queryParams['route'], $queryParams['id'])
+                    && (string)$this->firstEl['uid'] === (string)$queryParams['id']
                 ) {
-                    $returnUrl = $this->retUrl;
-                    if ($this->firstEl['table'] === 'pages') {
-                        parse_str((string)parse_url($returnUrl, PHP_URL_QUERY), $queryParams);
-                        if (isset($queryParams['route'])
-                            && isset($queryParams['id'])
-                            && (string)$this->firstEl['uid'] === (string)$queryParams['id']
-                        ) {
-                            // TODO: Use the page's pid instead of 0, this requires a clean API to manipulate the page
-                            // tree from the outside to be able to mark the pid as active
-                            $returnUrl = (string)$uriBuilder->buildUriFromRoutePath($queryParams['route'], ['id' => 0]);
-                        }
-                    }
-                    $deleteButton = $buttonBar->makeLinkButton()
-                        ->setHref('#')
-                        ->setClasses('t3js-editform-delete-record')
-                        ->setTitle($lang->getLL('deleteItem'))
-                        ->setIcon($this->moduleTemplate->getIconFactory()->getIcon(
-                            'actions-edit-delete',
-                            Icon::SIZE_SMALL
-                        ))
-                        ->setDataAttributes([
-                            'return-url' => $returnUrl,
-                            'uid' => $this->firstEl['uid'],
-                            'table' => $this->firstEl['table']
-                        ]);
-                    $buttonBar->addButton($deleteButton, ButtonBar::BUTTON_POSITION_LEFT, 4);
-                }
-                // Undo:
-                if (!empty($this->firstEl['table']) && $this->getTsConfigOption($this->firstEl['table'], 'showHistory')) {
-                    $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
-                        ->getQueryBuilderForTable('sys_history');
-                    $undoButtonR = $queryBuilder->select('tstamp')
-                        ->from('sys_history')
-                        ->where(
-                            $queryBuilder->expr()->eq(
-                                'tablename',
-                                $queryBuilder->createNamedParameter($this->firstEl['table'], \PDO::PARAM_STR)
-                            ),
-                            $queryBuilder->expr()->eq(
-                                'recuid',
-                                $queryBuilder->createNamedParameter($this->firstEl['uid'], \PDO::PARAM_INT)
-                            )
-                        )
-                        ->orderBy('tstamp', 'DESC')
-                        ->setMaxResults(1)
-                        ->execute()
-                        ->fetch();
-                    if ($undoButtonR !== false) {
-                        $aOnClick = 'window.location.href=' .
-                            GeneralUtility::quoteJSvalue(
-                                (string)$uriBuilder->buildUriFromRoute(
-                                    'record_history',
-                                    [
-                                        'element' => $this->firstEl['table'] . ':' . $this->firstEl['uid'],
-                                        'revert' => 'ALL_FIELDS',
-                                        'returnUrl' => $this->R_URI,
-                                    ]
-                                )
-                            ) . '; return false;';
-
-                        $undoButton = $buttonBar->makeLinkButton()
-                            ->setHref('#')
-                            ->setOnClick($aOnClick)
-                            ->setTitle(
-                                sprintf(
-                                    $lang->getLL('undoLastChange'),
-                                    BackendUtility::calcAge(
-                                        $GLOBALS['EXEC_TIME'] - $undoButtonR['tstamp'],
-                                        $lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.minutesHoursDaysYears')
-                                    )
-                                )
-                            )
-                            ->setIcon($this->moduleTemplate->getIconFactory()->getIcon(
-                                'actions-document-history-open',
-                                Icon::SIZE_SMALL
-                            ));
-                        $buttonBar->addButton($undoButton, ButtonBar::BUTTON_POSITION_LEFT, 3);
-                    }
-                }
-                if (!empty($this->firstEl['table']) && $this->getTsConfigOption($this->firstEl['table'], 'showHistory')) {
-                    $aOnClick = 'window.location.href=' .
-                        GeneralUtility::quoteJSvalue(
-                            (string)$uriBuilder->buildUriFromRoute(
-                                'record_history',
-                                [
-                                    'element' => $this->firstEl['table'] . ':' . $this->firstEl['uid'],
-                                    'returnUrl' => $this->R_URI,
-                                ]
-                            )
-                        ) . '; return false;';
-
-                    $historyButton = $buttonBar->makeLinkButton()
-                        ->setHref('#')
-                        ->setOnClick($aOnClick)
-                        ->setTitle('Open history of this record')
-                        ->setIcon($this->moduleTemplate->getIconFactory()->getIcon(
-                            'actions-document-history-open',
-                            Icon::SIZE_SMALL
-                        ));
-                    $buttonBar->addButton($historyButton, ButtonBar::BUTTON_POSITION_LEFT, 3);
-                }
-                // If only SOME fields are shown in the form, this will link the user to the FULL form:
-                if ($this->columnsOnly) {
-                    $columnsOnlyButton = $buttonBar->makeLinkButton()
-                        ->setHref($this->R_URI . '&columnsOnly=')
-                        ->setTitle($lang->getLL('editWholeRecord'))
-                        ->setIcon($this->moduleTemplate->getIconFactory()->getIcon(
-                            'actions-open',
-                            Icon::SIZE_SMALL
-                        ));
-                    $buttonBar->addButton($columnsOnlyButton, ButtonBar::BUTTON_POSITION_LEFT, 3);
+
+                    /** @var UriBuilder $uriBuilder */
+                    $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
+
+                    // TODO: Use the page's pid instead of 0, this requires a clean API to manipulate the page
+                    // tree from the outside to be able to mark the pid as active
+                    $returnUrl = (string)$uriBuilder->buildUriFromRoutePath($queryParams['route'], ['id' => 0]);
                 }
             }
+
+            /** @var ReferenceIndex $referenceIndex */
+            $referenceIndex = GeneralUtility::makeInstance(ReferenceIndex::class);
+            $numberOfReferences = $referenceIndex->getNumberOfReferencedRecords(
+                $this->firstEl['table'],
+                (int)$this->firstEl['uid']
+            );
+
+            $referenceCountMessage = BackendUtility::referenceCount(
+                $this->firstEl['table'],
+                (int)$this->firstEl['uid'],
+                $this->getLanguageService()->sL(
+                    'LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.referencesToRecord'
+                ),
+                $numberOfReferences
+            );
+            $translationCountMessage = BackendUtility::translationCount(
+                $this->firstEl['table'],
+                (int)$this->firstEl['uid'],
+                $this->getLanguageService()->sL(
+                    'LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.translationsOfRecord'
+                )
+            );
+
+            $deleteButton = $buttonBar->makeLinkButton()
+                ->setClasses($classNames)
+                ->setDataAttributes([
+                    'return-url' => $returnUrl,
+                    'uid' => $this->firstEl['uid'],
+                    'table' => $this->firstEl['table'],
+                    'reference-count-message' => $referenceCountMessage,
+                    'translation-count-message' => $translationCountMessage
+                ])
+                ->setHref('#')
+                ->setIcon($this->moduleTemplate->getIconFactory()->getIcon(
+                   'actions-edit-delete',
+                   Icon::SIZE_SMALL
+                ))
+                ->setShowLabelText(true)
+                ->setTitle($this->getLanguageService()->getLL('deleteItem'));
+
+            $buttonBar->addButton($deleteButton, $position, $group);
         }
-        $cshButton = $buttonBar->makeHelpButton()->setModuleName('xMOD_csh_corebe')->setFieldName('TCEforms');
-        $buttonBar->addButton($cshButton);
+    }
+
+    /**
+     * Register the history button to the button bar
+     *
+     * @param ButtonBar $buttonBar
+     * @param string $position
+     * @param int $group
+     */
+    protected function registerHistoryButtonToButtonBar(ButtonBar $buttonBar, string $position, int $group)
+    {
+        if (
+            count($this->elementsData) === 1
+            && !empty($this->firstEl['table'])
+            && $this->getTsConfigOption($this->firstEl['table'], 'showHistory')
+        ) {
+            /** @var UriBuilder $uriBuilder */
+            $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
+
+            $historyButtonOnClick = 'window.location.href=' .
+                GeneralUtility::quoteJSvalue(
+                    (string)$uriBuilder->buildUriFromRoute(
+                        'record_history',
+                        [
+                            'element' => $this->firstEl['table'] . ':' . $this->firstEl['uid'],
+                            'returnUrl' => $this->R_URI,
+                        ]
+                    )
+                ) . '; return false;';
 
+            $historyButton = $buttonBar->makeLinkButton()
+                ->setHref('#')
+                ->setIcon($this->moduleTemplate->getIconFactory()->getIcon(
+                    'actions-document-history-open',
+                    Icon::SIZE_SMALL
+                ))
+                ->setOnClick($historyButtonOnClick)
+                ->setTitle('Open history of this record')
+                ;
+
+            $buttonBar->addButton($historyButton, $position, $group);
+        }
+    }
+
+    /**
+     * Register the columns only button to the button bar
+     *
+     * @param ButtonBar $buttonBar
+     * @param string $position
+     * @param int $group
+     */
+    protected function registerColumnsOnlyButtonToButtonBar(ButtonBar $buttonBar, string $position, int $group)
+    {
+        if (
+            $this->columnsOnly
+            && count($this->elementsData) === 1
+        ) {
+            $columnsOnlyButton = $buttonBar->makeLinkButton()
+                ->setHref($this->R_URI . '&columnsOnly=')
+                ->setTitle($this->getLanguageService()->getLL('editWholeRecord'))
+                ->setIcon($this->moduleTemplate->getIconFactory()->getIcon(
+                    'actions-open',
+                    Icon::SIZE_SMALL
+                ));
+
+            $buttonBar->addButton($columnsOnlyButton, $position, $group);
+        }
+    }
+
+    /**
+     * Register the open in new window button to the button bar
+     *
+     * @param ButtonBar $buttonBar
+     * @param string $position
+     * @param int $group
+     */
+    protected function registerOpenInNewWindowButtonToButtonBar(ButtonBar $buttonBar, string $position, int $group)
+    {
         $closeUrl = $this->getCloseUrl();
         if ($this->returnUrl !== $closeUrl) {
-            $shortCutButton = $this->moduleTemplate->getDocHeaderComponent()->getButtonBar()->makeShortcutButton();
-            $shortCutButton->setModuleName('xMOD_alt_doc.php')
-                ->setGetVariables([
-                    'returnUrl',
-                    'edit',
-                    'defVals',
-                    'overrideVals',
-                    'columnsOnly',
-                    'returnNewPageId',
-                    'noView']);
-            $this->moduleTemplate->getDocHeaderComponent()->getButtonBar()->addButton($shortCutButton);
-
             $requestUri = GeneralUtility::linkThisScript([
                 'returnUrl' => $closeUrl,
             ]);
@@ -1637,37 +1927,55 @@ class EditDocumentController
                 . GeneralUtility::quoteJSvalue($requestUri) . ','
                 . GeneralUtility::quoteJSvalue(md5($this->R_URI))
                 . ',\'width=670,height=500,status=0,menubar=0,scrollbars=1,resizable=1\');vHWin.focus();return false;';
+
             $openInNewWindowButton = $this->moduleTemplate->getDocHeaderComponent()->getButtonBar()
                 ->makeLinkButton()
                 ->setHref('#')
                 ->setTitle($this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.openInNewWindow'))
                 ->setIcon($this->moduleTemplate->getIconFactory()->getIcon('actions-window-open', Icon::SIZE_SMALL))
                 ->setOnClick($aOnClick);
-            $this->moduleTemplate->getDocHeaderComponent()->getButtonBar()->addButton(
-                $openInNewWindowButton,
-                ButtonBar::BUTTON_POSITION_RIGHT
-            );
+
+            $buttonBar->addButton($openInNewWindowButton, $position, $group);
         }
     }
 
     /**
-     * Check if the page is in free translation mode
+     * Register the shortcut button to the button bar
      *
-     * @param int $page
-     * @param int $column
-     * @param int $language
-     * @return bool
+     * @param ButtonBar $buttonBar
+     * @param string $position
+     * @param int $group
      */
-    protected function isPageInFreeTranslationMode(int $page, int $column, int $language): bool
+    protected function registerShortcutButtonToButtonBar(ButtonBar $buttonBar, string $position, int $group)
     {
-        $freeTranslationMode = false;
+        if ($this->returnUrl !== $this->getCloseUrl()) {
+            $shortCutButton = $this->moduleTemplate->getDocHeaderComponent()->getButtonBar()->makeShortcutButton();
+            $shortCutButton->setModuleName('xMOD_alt_doc.php')
+                ->setGetVariables([
+                    'returnUrl',
+                    'edit',
+                    'defVals',
+                    'overrideVals',
+                    'columnsOnly',
+                    'returnNewPageId',
+                    'noView']);
 
-        if ($this->getConnectedContentElementTranslationsCount($page, $column, $language) === 0
-            && $this->getStandAloneContentElementTranslationsCount($page, $column, $language) >= 0) {
-            $freeTranslationMode = true;
+            $buttonBar->addButton($shortCutButton, $position, $group);
         }
+    }
 
-        return $freeTranslationMode;
+    /**
+     * Register the CSH button to the button bar
+     *
+     * @param ButtonBar $buttonBar
+     * @param string $position
+     * @param int $group
+     */
+    protected function registerCshButtonToButtonBar(ButtonBar $buttonBar, string $position, int $group)
+    {
+        $cshButton = $buttonBar->makeHelpButton()->setModuleName('xMOD_csh_corebe')->setFieldName('TCEforms');
+
+        $buttonBar->addButton($cshButton, $position, $group);
     }
 
     /**
index 4460942..a97d36e 100644 (file)
@@ -44,6 +44,13 @@ class AbstractButton extends AbstractControl implements ButtonInterface
     protected $showLabelText = false;
 
     /**
+     * Disabled state of the button
+     *
+     * @var bool
+     */
+    protected $disabled = false;
+
+    /**
      * Show Label text
      *
      * @return bool
@@ -100,6 +107,28 @@ class AbstractButton extends AbstractControl implements ButtonInterface
     }
 
     /**
+     * Check if button is disabled
+     *
+     * @return bool
+     */
+    public function isDisabled(): bool
+    {
+        return $this->disabled;
+    }
+
+    /**
+     * Set if button needs to be disabled
+     *
+     * @param bool $disabled
+     * @return AbstractButton
+     */
+    public function setDisabled(bool $disabled): AbstractButton
+    {
+        $this->disabled = $disabled;
+        return $this;
+    }
+
+    /**
      * Implementation from ButtonInterface
      * This object is an abstract, so no implementation is necessary
      *
index 1703df2..04757de 100644 (file)
@@ -154,6 +154,9 @@ class InputButton extends AbstractButton
             'title' => $this->getTitle(),
             'form' => trim($this->getForm())
         ];
+        if ($this->isDisabled()) {
+            $attributes['disabled'] = 'disabled';
+        }
         $labelText = '';
         if ($this->showLabelText) {
             $labelText = ' ' . $this->title;
index 7a4b0ce..ffa93e5 100644 (file)
@@ -2549,8 +2549,7 @@ class BackendUtility
 
     /**
      * Returns a JavaScript string for viewing the page id, $id
-     * It will detect the correct domain name if needed and provide the link with the right back path.
-     * Also it will re-use any window already open.
+     * It will re-use any window already open.
      *
      * @param int $pageUid Page UID
      * @param string $backPath Must point back to TYPO3_mainDir (where the site is assumed to be one level above)
@@ -2570,6 +2569,46 @@ class BackendUtility
         $additionalGetVars = '',
         $switchFocus = true
     ) {
+        $previewUrl = self::getPreviewUrl(
+            $pageUid,
+            $backPath,
+            $rootLine,
+            $anchorSection,
+            $alternativeUrl,
+            $additionalGetVars,
+            $switchFocus
+        );
+
+        $onclickCode = 'var previewWin = window.open(' . GeneralUtility::quoteJSvalue($previewUrl) . ',\'newTYPO3frontendWindow\');'
+            . ($switchFocus ? 'previewWin.focus();' : '') . LF
+            . 'if (previewWin.location.href === ' . GeneralUtility::quoteJSvalue($previewUrl) . ') { previewWin.location.reload(); };';
+
+        return $onclickCode;
+    }
+
+    /**
+     * Returns the preview url
+     *
+     * It will detect the correct domain name if needed and provide the link with the right back path.
+     *
+     * @param int $pageUid Page UID
+     * @param string $backPath Must point back to TYPO3_mainDir (where the site is assumed to be one level above)
+     * @param array|null $rootLine If root line is supplied the function will look for the first found domain record and use that URL instead (if found)
+     * @param string $anchorSection Optional anchor to the URL
+     * @param string $alternativeUrl An alternative URL that, if set, will ignore other parameters except $switchFocus: It will return the window.open command wrapped around this URL!
+     * @param string $additionalGetVars Additional GET variables.
+     * @param bool $switchFocus If TRUE, then the preview window will gain the focus.
+     * @return string
+     */
+    public static function getPreviewUrl(
+        $pageUid,
+        $backPath = '',
+        $rootLine = null,
+        $anchorSection = '',
+        $alternativeUrl = '',
+        $additionalGetVars = '',
+        &$switchFocus = true
+    ): string {
         $viewScript = '/index.php?id=';
         if ($alternativeUrl) {
             $viewScript = $alternativeUrl;
@@ -2627,9 +2666,7 @@ class BackendUtility
             }
         }
 
-        $onclickCode = 'var previewWin = window.open(' . GeneralUtility::quoteJSvalue($previewUrl) . ',\'newTYPO3frontendWindow\');' . ($switchFocus ? 'previewWin.focus();' : '') . LF
-            . 'if (previewWin.location.href === ' . GeneralUtility::quoteJSvalue($previewUrl) . ') { previewWin.location.reload(); };';
-        return $onclickCode;
+        return $previewUrl;
     }
 
     /**
index 7254e20..b91bbb6 100644 (file)
                        <trans-unit id="buttons.confirm.duplicate_record_changed.cancel">
                                <source>Cancel</source>
                        </trans-unit>
-                       <trans-unit id="button.confirm.duplicate_record_changed.dismiss_and_duplicate">
-                               <source>Dismiss changes and duplicate</source>
+                       <trans-unit id="button.confirm.duplicate_record_changed.no">
+                               <source>No, just duplicate the original</source>
                        </trans-unit>
-                       <trans-unit id="buttons.confirm.duplicate_record_changed.save_and_duplicate">
-                               <source>Save changes and duplicate</source>
+                       <trans-unit id="buttons.confirm.duplicate_record_changed.yes">
+                               <source>Yes, save and duplicate this record</source>
                        </trans-unit>
                        <trans-unit id="buttons.confirm.close_without_save.yes">
                                <source>Yes, discard my changes</source>
                                <source>No, I will continue editing</source>
                        </trans-unit>
                        <trans-unit id="label.confirm.duplicate_record_changed.title">
-                               <source>Duplicate changed record?</source>
+                               <source>Do you want to save before duplicating this record?</source>
                        </trans-unit>
                        <trans-unit id="label.confirm.duplicate_record_changed.content">
-                               <source>Do you want to save your changes and duplicate the record?</source>
+                               <source>You currently have unsaved changes. Do you want to save your changes before duplicating this record?</source>
+                       </trans-unit>
+                       <trans-unit id="label.confirm.new_record_changed.title">
+                               <source>Do you want to save before adding?</source>
+                       </trans-unit>
+                       <trans-unit id="label.confirm.new_record_changed.content">
+                               <source>You need to save your changes before creating a new record. Do you want to save and create now?</source>
+                       </trans-unit>
+                       <trans-unit id="buttons.confirm.new_record_changed.cancel">
+                               <source>Cancel</source>
+                       </trans-unit>
+                       <trans-unit id="buttons.confirm.new_record_changed.no">
+                               <source>No, just add</source>
+                       </trans-unit>
+                       <trans-unit id="buttons.confirm.new_record_changed.yes">
+                               <source>Yes, save and create now</source>
+                       </trans-unit>
+                       <trans-unit id="label.confirm.view_record_changed.title">
+                               <source>Do you want to save before viewing?</source>
+                       </trans-unit>
+                       <trans-unit id="label.confirm.view_record_changed.content">
+                               <source>You currently have unsaved changes. You can either discard these changes or save and view them.</source>
+                       </trans-unit>
+                       <trans-unit id="label.confirm.view_record_changed.content.is-new-page">
+                               <source>You need to save your changes before viewing the page. Do you want to save and view them now?</source>
+                       </trans-unit>
+                       <trans-unit id="buttons.confirm.view_record_changed.cancel">
+                               <source>Cancel</source>
+                       </trans-unit>
+                       <trans-unit id="buttons.confirm.view_record_changed.no-save">
+                               <source>View without changes</source>
+                       </trans-unit>
+                       <trans-unit id="buttons.confirm.view_record_changed.save">
+                               <source>Save changes and view</source>
                        </trans-unit>
                        <trans-unit id="label.confirm.close_without_save.title">
-                               <source>Do you want to quit without saving?</source>
+                               <source>Do you want to close without saving?</source>
                        </trans-unit>
                        <trans-unit id="label.confirm.close_without_save.content">
-                               <source>You have currently unsaved changes. Are you sure that you want to discard all changes?</source>
+                               <source>You currently have unsaved changes. Are you sure you want to discard these changes?</source>
                        </trans-unit>
                        <trans-unit id="buttons.confirm.delete_file.no">
                                <source>Cancel</source>
index 0ac3f1f..936d4d1 100644 (file)
@@ -643,80 +643,22 @@ define(['jquery',
         });
       }
     }).on('click', '.t3js-editform-close', function(e) {
+        e.preventDefault();
+        FormEngine.preventExitIfNotSaved(
+            FormEngine.preventExitIfNotSavedCallback
+        );
+    }).on('click', '.t3js-editform-view', function(e) {
       e.preventDefault();
-      FormEngine.preventExitIfNotSaved(
-        FormEngine.preventExitIfNotSavedCallback
-      );
+      FormEngine.previewAction(e, FormEngine.previewActionCallback);
+    }).on('click', '.t3js-editform-new', function(e) {
+      e.preventDefault();
+      FormEngine.newAction(e, FormEngine.newActionCallback);
     }).on('click', '.t3js-editform-duplicate', function(e) {
-        e.preventDefault();
-        var $elem = $('<input />').attr('type', 'hidden').attr('name', '_duplicatedoc').attr('value', '1');
-        if ($('form[name="' + FormEngine.formName + '"] .has-change').length > 0) {
-          var title = TYPO3.lang['label.confirm.duplicate_record_changed.title'] || 'Duplicate changed record?';
-          var content = TYPO3.lang['label.confirm.duplicate_record_changed.content'] || 'Do you want to save your changes and duplicate the record?';
-          var $modal = Modal.confirm(title, content, Severity.warning, [
-            {
-              text: TYPO3.lang['buttons.confirm.duplicate_record_changed.cancel'] || 'Cancel',
-              active: true,
-              btnClass: 'btn-default',
-              name: 'cancel'
-            },
-            {
-              text: TYPO3.lang['buttons.confirm.duplicate_record_changed.dismiss_and_duplicate'] || 'Dismiss changes and duplicate',
-              active: true,
-              btnClass: 'btn-default',
-              name: 'dismissDuplicate'
-            },
-            {
-              text: TYPO3.lang['buttons.confirm.duplicate_record_changed.save_and_duplicate'] || 'Save changes and duplicate',
-              btnClass: 'btn-success',
-              name: 'saveDuplicate'
-            }
-        ]);
-          $modal.on('button.clicked', function (e) {
-            if (e.target.name === 'cancel') {
-              Modal.dismiss();
-            } else if (e.target.name === 'dismissDuplicate') {
-              $('form[name=' + FormEngine.formName + ']').append($elem);
-              $('input[name=doSave]').val(0);
-              Modal.dismiss();
-              document.editform.submit();
-            } else if (e.target.name == 'saveDuplicate') {
-              $('form[name=' + FormEngine.formName + ']').append($elem);
-              $('input[name=doSave]').val(1);
-              Modal.dismiss();
-              document.editform.submit();
-            }
-          });
-        } else {
-          $('form[name=' + FormEngine.formName + ']').append($elem);
-          document.editform.submit();
-        }
+      e.preventDefault();
+      FormEngine.duplicateAction(e, FormEngine.duplicateActionCallback);
     }).on('click', '.t3js-editform-delete-record', function(e) {
       e.preventDefault();
-      var title = TYPO3.lang['label.confirm.delete_record.title'] || 'Delete this record?';
-      var content = TYPO3.lang['label.confirm.delete_record.content'] || 'Are you sure you want to delete this record?';
-      var $anchorElement = $(this);
-      var $modal = Modal.confirm(title, content, Severity.warning, [
-        {
-          text: TYPO3.lang['buttons.confirm.delete_record.no'] || 'Cancel',
-          active: true,
-          btnClass: 'btn-default',
-          name: 'no'
-        },
-        {
-          text: TYPO3.lang['buttons.confirm.delete_record.yes'] || 'Yes, delete this record',
-          btnClass: 'btn-warning',
-          name: 'yes'
-        }
-      ]);
-      $modal.on('button.clicked', function(e) {
-        if (e.target.name === 'no') {
-          Modal.dismiss();
-        } else if (e.target.name === 'yes') {
-          deleteRecord($anchorElement.data('table'), $anchorElement.data('uid'), $anchorElement.data('return-url'));
-          Modal.dismiss();
-        }
-      });
+      FormEngine.deleteAction(e, FormEngine.deleteActionCallback);
     }).on('click', '.t3js-editform-delete-inline-record', function(e) {
       e.preventDefault();
       var title = TYPO3.lang['label.confirm.delete_record.title'] || 'Delete this record?';
@@ -788,6 +730,9 @@ define(['jquery',
     }).on('formengine.dp.change', function(event, $field) {
       FormEngine.Validation.validate();
       FormEngine.Validation.markFieldAsChanged($field);
+      $('.module-docheader-bar .btn').removeClass('disabled').prop('disabled', false);
+    }).on('change', function(event) {
+      $('.module-docheader-bar .btn').removeClass('disabled').prop('disabled', false);
     });
   };
 
@@ -1156,7 +1101,9 @@ define(['jquery',
    * @return {boolean}
    */
   FormEngine.hasChange = function() {
-    return $('form[name="' + FormEngine.formName + '"] .has-change').length > 0;
+    var formElementChanges = $('form[name="' + FormEngine.formName + '"] .has-change').length > 0,
+        inlineRecordChanges = $('[name^="data["].has-change').length > 0;
+    return formElementChanges || inlineRecordChanges;
   };
 
   /**
@@ -1177,19 +1124,25 @@ define(['jquery',
     callback = callback || FormEngine.preventExitIfNotSavedCallback;
 
     if (FormEngine.hasChange()) {
-      var title = TYPO3.lang['label.confirm.close_without_save.title'] || 'Do you want to quit without saving?';
-      var content = TYPO3.lang['label.confirm.close_without_save.content'] || 'You have currently unsaved changes. Are you sure that you want to discard all changes?';
+      var title = TYPO3.lang['label.confirm.close_without_save.title'] || 'Do you want to close without saving?';
+      var content = TYPO3.lang['label.confirm.close_without_save.content'] || 'You currently have unsaved changes. Are you sure you want to discard these changes?';
+      var $elem = $('<input />').attr('type', 'hidden').attr('name', '_saveandclosedok').attr('value', '1');
       var $modal = Modal.confirm(title, content, Severity.warning, [
         {
           text: TYPO3.lang['buttons.confirm.close_without_save.no'] || 'No, I will continue editing',
-          active: true,
           btnClass: 'btn-default',
           name: 'no'
         },
         {
           text: TYPO3.lang['buttons.confirm.close_without_save.yes'] || 'Yes, discard my changes',
-          btnClass: 'btn-warning',
+          btnClass: 'btn-default',
           name: 'yes'
+        },
+        {
+          text: TYPO3.lang['buttons.confirm.save and close'] || 'Save and close',
+          btnClass: 'btn-warning',
+          name: 'save',
+          active: true
         }
       ]);
       $modal.on('button.clicked', function(e) {
@@ -1199,6 +1152,11 @@ define(['jquery',
         } else if (e.target.name === 'yes') {
           Modal.dismiss();
           callback.call(null, true);
+        } else if (e.target.name === 'save') {
+          $('form[name=' + FormEngine.formName + ']').append($elem);
+          $('input[name=doSave]').val(1);
+          Modal.dismiss();
+          document.editform.submit();
         }
       });
     } else {
@@ -1231,6 +1189,366 @@ define(['jquery',
   };
 
   /**
+   * Preview action
+   *
+   * When there are changes:
+   * Will take action based on local storage preset
+   * If preset is not available, a modal will open
+   *
+   * @param {Event} event
+   * @param {Function} callback
+   */
+  FormEngine.previewAction = function(event, callback) {
+    callback = callback || FormEngine.previewActionCallback;
+
+    var previewUrl = event.target.href;
+    var isNew = event.target.dataset.hasOwnProperty('isNew');
+    var $actionElement = $('<input />').attr('type', 'hidden').attr('name', '_savedokview').attr('value', '1');
+    if (FormEngine.hasChange()) {
+      FormEngine.showPreviewModal(previewUrl, isNew, $actionElement, callback);
+    } else {
+      $('form[name=' + FormEngine.formName + ']').append($actionElement);
+      window.open('', 'newTYPO3frontendWindow');
+      document.editform.submit();
+    }
+  };
+
+  /**
+   * The callback for the preview action
+   *
+   * @param {string} modalButtonName
+   * @param {string} previewUrl
+   * @param {element} $actionElement
+   */
+  FormEngine.previewActionCallback = function(modalButtonName, previewUrl, $actionElement) {
+    Modal.dismiss();
+    switch(modalButtonName) {
+      case 'discard':
+        var previewWin = window.open(previewUrl, 'newTYPO3frontendWindow');
+        previewWin.focus();
+        if (previewWin.location.href === previewUrl) {
+          previewWin.location.reload();
+        }
+        break;
+      case 'save':
+        $('form[name=' + FormEngine.formName + ']').append($actionElement);
+        $('input[name=doSave]').val(1);
+        window.open('', 'newTYPO3frontendWindow');
+        document.editform.submit();
+        break;
+    }
+  };
+
+  /**
+   * Show the preview modal
+   *
+   * @param {string} previewUrl
+   * @param {bool} isNew
+   * @param {element} $actionElement
+   * @param {Function} callback
+   */
+  FormEngine.showPreviewModal = function(previewUrl, isNew, $actionElement, callback) {
+    var title = TYPO3.lang['label.confirm.view_record_changed.title'] || 'Do you want to save before viewing?';
+    var modalCancelButtonConfiguration = {
+      text: TYPO3.lang['buttons.confirm.view_record_changed.cancel'] || 'Cancel',
+      btnClass: 'btn-default',
+      name: 'cancel'
+    };
+    var modaldismissViewButtonConfiguration = {
+      text: TYPO3.lang['buttons.confirm.view_record_changed.no-save'] || 'View without changes',
+      btnClass: 'btn-info',
+      name: 'discard'
+    };
+    var modalsaveViewButtonConfiguration = {
+      text: TYPO3.lang['buttons.confirm.view_record_changed.save'] || 'Save changes and view',
+      btnClass: 'btn-info',
+      name: 'save',
+      active: true
+    };
+    var modalButtons = [];
+    var content = '';
+    if (isNew) {
+      modalButtons = [
+        modalCancelButtonConfiguration,
+        modalsaveViewButtonConfiguration
+      ];
+      content = (
+        TYPO3.lang['label.confirm.view_record_changed.content.is-new-page']
+        || 'You need to save your changes before viewing the page. Do you want to save and view them now?'
+      );
+    } else {
+      modalButtons = [
+        modalCancelButtonConfiguration,
+        modaldismissViewButtonConfiguration,
+        modalsaveViewButtonConfiguration
+      ];
+      content = (
+        TYPO3.lang['label.confirm.view_record_changed.content']
+        || 'You currently have unsaved changes. You can either discard these changes or save and view them.'
+      )
+    }
+    var $modal = Modal.confirm(title, content, Severity.info, modalButtons);
+    $modal.on('button.clicked', function (event) {
+      callback(event.target.name, previewUrl, $actionElement, $modal);
+    });
+  };
+
+  /**
+   * New action
+   *
+   * When there are changes:
+   * Will take action based on local storage preset
+   * If preset is not available, a modal will open
+   *
+   * @param {Event} event
+   * @param {Function} callback
+   */
+  FormEngine.newAction = function(event, callback) {
+    callback = callback || FormEngine.newActionCallback;
+
+    var $actionElement = $('<input />').attr('type', 'hidden').attr('name', '_savedoknew').attr('value', '1');
+    var isNew = event.target.dataset.hasOwnProperty('isNew');
+    if (FormEngine.hasChange()) {
+      FormEngine.showNewModal(isNew, $actionElement, callback);
+    } else {
+      $('form[name=' + FormEngine.formName + ']').append($actionElement);
+      document.editform.submit();
+    }
+  };
+
+  /**
+   * The callback for the preview action
+   *
+   * @param {string} modalButtonName
+   * @param {element} $actionElement
+   */
+  FormEngine.newActionCallback = function(modalButtonName, $actionElement) {
+    var $form = $('form[name=' + FormEngine.formName + ']');
+    Modal.dismiss();
+    switch(modalButtonName) {
+      case 'no':
+        $form.append($actionElement);
+        document.editform.submit();
+        break;
+      case 'yes':
+        $form.append($actionElement);
+        $('input[name=doSave]').val(1);
+        document.editform.submit();
+        break;
+    }
+  };
+
+  /**
+   * Show the new modal
+   *
+   * @param {element} $actionElement
+   * @param {Function} callback
+   * @param {bool} isNew
+   */
+  FormEngine.showNewModal = function(isNew, $actionElement, callback) {
+    var title = TYPO3.lang['label.confirm.new_record_changed.title'] || 'Do you want to save before adding?';
+    var content = (
+      TYPO3.lang['label.confirm.new_record_changed.content']
+      || 'You need to save your changes before creating a new record. Do you want to save and create now?'
+    );
+    var modalButtons = [];
+    var modalCancelButtonConfiguration = {
+      text: TYPO3.lang['buttons.confirm.new_record_changed.cancel'] || 'Cancel',
+      btnClass: 'btn-default',
+      name: 'cancel'
+    };
+    var modalNoButtonConfiguration = {
+      text: TYPO3.lang['buttons.confirm.new_record_changed.no'] || 'No, just add',
+      btnClass: 'btn-default',
+      name: 'no'
+    };
+    var modalYesButtonConfiguration = {
+      text: TYPO3.lang['buttons.confirm.new_record_changed.yes'] || 'Yes, save and create now',
+      btnClass: 'btn-info',
+      name: 'yes',
+      active: true
+    };
+    if (isNew) {
+      modalButtons = [
+        modalCancelButtonConfiguration,
+        modalYesButtonConfiguration
+      ];
+    } else {
+      modalButtons = [
+        modalCancelButtonConfiguration,
+        modalNoButtonConfiguration,
+        modalYesButtonConfiguration
+      ];
+    }
+    var $modal = Modal.confirm(title, content, Severity.info, modalButtons);
+    $modal.on('button.clicked', function (event) {
+        callback(event.target.name, $actionElement);
+    });
+  };
+
+  /**
+   * Duplicate action
+   *
+   * When there are changes:
+   * Will take action based on local storage preset
+   * If preset is not available, a modal will open
+   *
+   * @param {Event} event
+   * @param {Function} callback
+   */
+  FormEngine.duplicateAction = function(event, callback) {
+    callback = callback || FormEngine.duplicateActionCallback;
+
+    var $actionElement = $('<input />').attr('type', 'hidden').attr('name', '_duplicatedoc').attr('value', '1');
+    var isNew = event.target.dataset.hasOwnProperty('isNew');
+    if (FormEngine.hasChange()) {
+        FormEngine.showDuplicateModal(isNew, $actionElement, callback);
+    } else {
+      $('form[name=' + FormEngine.formName + ']').append($actionElement);
+      document.editform.submit();
+    }
+  };
+
+  /**
+   * The callback for the duplicate action
+   *
+   * @param {string} modalButtonName
+   * @param {element} $actionElement
+   */
+  FormEngine.duplicateActionCallback = function(modalButtonName, $actionElement) {
+    var $form = $('form[name=' + FormEngine.formName + ']');
+    Modal.dismiss();
+    switch(modalButtonName) {
+      case 'no':
+        $form.append($actionElement);
+        document.editform.submit();
+        break;
+      case 'yes':
+        $form.append($actionElement);
+        $('input[name=doSave]').val(1);
+        document.editform.submit();
+        break;
+    }
+  };
+
+  /**
+   * Show the duplicate modal
+   *
+   * @param {bool} isNew
+   * @param {element} $actionElement
+   * @param {Function} callback
+   */
+  FormEngine.showDuplicateModal = function(isNew, $actionElement, callback) {
+    var title = TYPO3.lang['label.confirm.duplicate_record_changed.title'] || 'Do you want to save before duplicating this record?';
+    var content = (
+      TYPO3.lang['label.confirm.duplicate_record_changed.content']
+      || 'You currently have unsaved changes. Do you want to save your changes before duplicating this record?'
+    );
+    var modalButtons = [];
+    var modalCancelButtonConfiguration = {
+      text: TYPO3.lang['buttons.confirm.duplicate_record_changed.cancel'] || 'Cancel',
+      btnClass: 'btn-default',
+      name: 'cancel'
+    };
+    var modalDismissDuplicateButtonConfiguration = {
+      text: TYPO3.lang['buttons.confirm.duplicate_record_changed.no'] || 'No, just duplicate the original',
+      btnClass: 'btn-default',
+      name: 'no'
+    };
+    var modalSaveDuplicateButtonConfiguration = {
+      text: TYPO3.lang['buttons.confirm.duplicate_record_changed.yes'] || 'Yes, save and duplicate this record',
+      btnClass: 'btn-info',
+      name: 'yes',
+      active: true
+    };
+    if (isNew) {
+      modalButtons = [
+        modalCancelButtonConfiguration,
+        modalSaveDuplicateButtonConfiguration
+      ];
+    } else {
+      modalButtons = [
+        modalCancelButtonConfiguration,
+        modalDismissDuplicateButtonConfiguration,
+        modalSaveDuplicateButtonConfiguration
+      ];
+    }
+    var $modal = Modal.confirm(title, content, Severity.info, modalButtons);
+    $modal.on('button.clicked', function (event) {
+      callback(event.target.name, $actionElement);
+    });
+  };
+
+  /**
+   * Delete action
+   *
+   * When there are changes:
+   * Will take action based on local storage preset
+   * If preset is not available, a modal will open
+   *
+   * @param {Event} event
+   * @param {Function} callback
+   */
+  FormEngine.deleteAction = function(event, callback) {
+    callback = callback || FormEngine.deleteActionCallback;
+
+    var $anchorElement = $(event.target);
+
+    FormEngine.showDeleteModal($anchorElement, callback);
+  };
+
+  /**
+   * The callback for the delete action
+   *
+   * @param {string} modalButtonName
+   * @param {element} $anchorElement
+   */
+  FormEngine.deleteActionCallback = function(modalButtonName, $anchorElement) {
+    Modal.dismiss();
+    switch(modalButtonName) {
+      case 'yes':
+        deleteRecord($anchorElement.data('table'), $anchorElement.data('uid'), $anchorElement.data('return-url'));
+        break;
+    }
+  };
+
+  /**
+   * Show the delete modal
+   *
+   * @param {element} $anchorElement
+   * @param {Function} callback
+   */
+  FormEngine.showDeleteModal = function($anchorElement, callback) {
+      var title = TYPO3.lang['label.confirm.delete_record.title'] || 'Delete this record?';
+      var content = TYPO3.lang['label.confirm.delete_record.content'] || 'Are you sure you want to delete this record?';
+
+      if ($anchorElement.data('reference-count-message')) {
+        content += ' ' + $anchorElement.data('reference-count-message');
+      }
+
+      if ($anchorElement.data('translation-count-message')) {
+        content += ' ' + $anchorElement.data('translation-count-message');
+      }
+
+      var $modal = Modal.confirm(title, content, Severity.warning, [
+        {
+          text: TYPO3.lang['buttons.confirm.delete_record.no'] || 'Cancel',
+          btnClass: 'btn-default',
+          name: 'no'
+        },
+        {
+          text: TYPO3.lang['buttons.confirm.delete_record.yes'] || 'Yes, delete this record',
+          btnClass: 'btn-warning',
+          name: 'yes',
+          active: true
+        }
+      ]);
+      $modal.on('button.clicked', function (event) {
+        callback(event.target.name, $anchorElement);
+      });
+  };
+
+  /**
    * Close current open document
    */
   FormEngine.closeDocument = function() {
index e44975d..a183346 100644 (file)
@@ -574,6 +574,8 @@ var inline = {
         $(objectIdPrefix + records[current + 1 - cAdj] + '_div')
       );
       this.redrawSortingButtons(objectPrefix, records);
+      $(formObj).addClass('has-change');
+      $(document).trigger('change');
     }
 
     return false;
@@ -609,6 +611,8 @@ var inline = {
     if (inline.data.config && inline.data.config[objectId]) {
       var table = inline.data.config[objectId].table;
       inline.redrawSortingButtons(objectId + inline.structureSeparator + table, checked);
+      $(formObj).addClass('has-change');
+      $(document).trigger('change');
     }
   },
 
@@ -719,6 +723,9 @@ var inline = {
           records.push(newUid);
         }
         formObj[0].value = records.join(',');
+
+        $(formObj).addClass('has-change');
+        $(document).trigger('change');
       }
 
       this.redrawSortingButtons(objectPrefix, records);
index e891f37..0bb4b4a 100644 (file)
@@ -849,6 +849,9 @@ Do you want to refresh it now?</source>
                        <trans-unit id="rm.saveDoc">
                                <source>Save</source>
                        </trans-unit>
+                       <trans-unit id="rm.viewDoc">
+                               <source>View</source>
+                       </trans-unit>
                        <trans-unit id="rm.saveDocShow">
                                <source>Save and view page</source>
                        </trans-unit>
@@ -861,6 +864,9 @@ Do you want to refresh it now?</source>
                        <trans-unit id="rm.saveNewDoc">
                                <source>Save and create a new one</source>
                        </trans-unit>
+                       <trans-unit id="rm.newDoc">
+                               <source>New</source>
+                       </trans-unit>
                        <trans-unit id="rm.duplicateDoc">
                                <source>Duplicate</source>
                        </trans-unit>
index bc370d9..406d1e9 100644 (file)
@@ -181,6 +181,6 @@ class ElementsGroupCest
         $I->switchToIFrame('list_frame');
         $I->see('admin', 'select[data-formengine-input-name="data[tx_styleguide_elements_group][1][group_db_1]"]');
         $I->click('.btn-toolbar button.btn:nth-child(2)');
-        $I->click('li a[data-form="EditDocumentController"] span[data-identifier="actions-document-save-close"]');
+        $I->click('button[name="_savedok"]');
     }
 }
index 20fed44..d484bd8 100644 (file)
@@ -111,8 +111,10 @@ class TemplateCest
         $config = $I->grabTextFrom('//textarea[@data-formengine-input-name="data[sys_template][1][config]"]');
         $config = str_replace('HELLO WORLD!', 'Hello Acceptance Test!', $config);
         $I->fillField('//textarea[@data-formengine-input-name="data[sys_template][1][config]"]', $config);
-        $I->click('.btn-toolbar .btn-group.t3js-splitbutton button.btn:nth-child(2)');
-        $I->click('//a[@data-name="_saveandclosedok"]');
+
+        $I->click('//*/button[@name="_savedok"][1]');
+        $I->waitForElement('a.t3js-editform-close');
+        $I->click('a.t3js-editform-close');
 
         $I->wantTo('see the changed title');
         $I->waitForElement('.table-fit');