diff --git a/Classes/ContextMenu/ItemProvider.php b/Classes/ContextMenu/ItemProvider.php new file mode 100644 index 0000000..1610a6e --- /dev/null +++ b/Classes/ContextMenu/ItemProvider.php @@ -0,0 +1,64 @@ + [ + 'type' => 'item', + 'label' => 'Create new from page...', // you can use "LLL:" syntax here + 'iconIdentifier' => 'actions-document-info', + 'callbackAction' => 'createTask', //name of the function in the JS file + ], + ]; + + public function getPriority(): int + { + return 90; + } + + public function canHandle(): bool + { + return $this->table === 'pages'; + } + + public function addItems(array $items): array + { + $this->initDisabledItems(); + $localItems = $this->prepareItems($this->itemsConfiguration); + $items = $items + $localItems; + return $items; + } + + protected function getAdditionalAttributes(string $itemName): array + { + return [ + 'data-callback-module' => 'TYPO3/CMS/Sudhaus7Wizard/CreateWizardTaskWizard', + ]; + } + + protected function canRender(string $itemName, string $type): bool + { + if (in_array($itemName, $this->disabledItems, true)) { + return false; + } + + $canRender = false; + if ($itemName == 's7wizard') { + $canRender = $this->isSiteRoot(); + } + return $canRender; + } + + private function isSiteRoot(): bool + { + $pageRecord = BackendUtility::getRecord('pages', $this->identifier); + return ($pageRecord['is_siteroot'] ?? 0) === 1; + } +} diff --git a/Classes/Controller/Backend/CreateNewTaskAjaxController.php b/Classes/Controller/Backend/CreateNewTaskAjaxController.php new file mode 100644 index 0000000..aa3c372 --- /dev/null +++ b/Classes/Controller/Backend/CreateNewTaskAjaxController.php @@ -0,0 +1,169 @@ +uriBuilder = $uriBuilder; + } + + public function getTemplatesAction(ServerRequestInterface $request): ResponseInterface + { + $templates = []; + + /** @var WizardProcessInterface $registeredTemplateExtention */ + foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['Sudhaus7Wizard']['registeredTemplateExtentions'] ?? [] as $registeredTemplateExtension) { + /** @var WizardTemplateConfigInterface $configuration */ + $configuration = $registeredTemplateExtension::getWizardConfig(); + $templates[] = [ + 'title' => $configuration->getDescription(), + 'value' => $configuration->getExtension(), + ]; + } + + $success = count($templates) > 0; + + $view = GeneralUtility::makeInstance(StandaloneView::class); + $view->setTemplatePathAndFilename('EXT:sudhaus7_wizard/Resources/Private/Templates/Backend/GetTemplates.html'); + $view->assign('templates', $templates); + + $data = [ + 'success' => $success, + 'templates' => $templates, + 'html' => $view->render(), + ]; + + return new JsonResponse($data); + } + + public function getGeneralFieldsAction(ServerRequestInterface $request): ResponseInterface + { + $view = GeneralUtility::makeInstance(StandaloneView::class); + $view->setTemplatePathAndFilename('EXT:sudhaus7_wizard/Resources/Private/Templates/Backend/GeneralFields.html'); + $data = [ + 'html' => $view->render(), + ]; + + return new JsonResponse($data); + } + + public function getEditorFieldsAction(ServerRequestInterface $request): ResponseInterface + { + $view = GeneralUtility::makeInstance(StandaloneView::class); + $view->setTemplatePathAndFilename('EXT:sudhaus7_wizard/Resources/Private/Templates/Backend/EditorFields.html'); + $data = [ + 'html' => $view->render(), + ]; + + return new JsonResponse($data); + } + + public function getTemplateFieldsAction(ServerRequestInterface $request): ResponseInterface + { + /** @var WizardProcessInterface|null $template */ + $template = $request->getQueryParams()['template'] ?? null; + if ($template === null || !in_array(WizardProcessInterface::class, class_implements($template), true)) { + $data = [ + 'success' => false, + ]; + + return new JsonResponse($data); + } + $configuration = $template::getWizardConfig(); + $flexInfoFile = $configuration->getFlexinfoFile(); + if (!empty($flexInfoFile)) { + $realPathToFlexFile = GeneralUtility::getFileAbsFileName($flexInfoFile); + // @todo render/prepare Flex + } + $view = GeneralUtility::makeInstance(StandaloneView::class); + $view->setTemplatePathAndFilename('EXT:sudhaus7_wizard/Resources/Private/Templates/Backend/TemplateFields.html'); + $data = [ + 'html' => $view->render(), + ]; + + return new JsonResponse($data); + } + + public function createNewTask(ServerRequestInterface $request): ResponseInterface + { + $wizard = $this->getParamsFromRequest($request)['wizard'] ?? []; + $wizard['pid'] = 0; + $newWizardIdentifier = StringUtility::getUniqueId('NEW'); + $data = [ + 'tx_sudhaus7wizard_domain_model_creator' => [ + $newWizardIdentifier => $wizard, + ], + ]; + + $dataHandler = GeneralUtility::makeInstance(DataHandler::class); + $dataHandler->start($data, []); + $dataHandler->process_datamap(); + if ($dataHandler->errorLog !== []) { + $returnData = [ + 'success' => false, + 'error' => [], + ]; + foreach ($dataHandler->errorLog as $errorLog) { + $returnData['error'][] = $errorLog; + } + return new JsonResponse($returnData); + } + + $newWizardTaskId = $dataHandler->substNEWwithIDs[$newWizardIdentifier]; + + $redirectUri = $this->uriBuilder + ->buildUriFromRoute( + 'record_edit', + [ + 'edit' => [ + 'tt_address' => [ + $newWizardTaskId => 'edit', + ], + ], + ] + ); + $returnData = [ + 'success' => true, + 'redirectUrl' => (string)$redirectUri, + ]; + return new JsonResponse($returnData); + } + + /** + * @return array + */ + protected function getParamsFromRequest(ServerRequestInterface $request): array + { + $postParams = $request->getParsedBody(); + + try { + $postArrayParams = match (gettype($postParams)) { + 'array' => $postParams, + 'object' => json_decode(json_encode($postParams) ?: '{}', true, 512, JSON_THROW_ON_ERROR), + default => [] + }; + } catch (\JsonException $_) { + $postArrayParams = []; + } + + return $postArrayParams; + } +} diff --git a/Configuration/Backend/AjaxRoutes.php b/Configuration/Backend/AjaxRoutes.php new file mode 100644 index 0000000..60e274e --- /dev/null +++ b/Configuration/Backend/AjaxRoutes.php @@ -0,0 +1,23 @@ + [ + 'path' => '/s7wizard/getTemplates', + 'method' => ['GET'], + 'target' => CreateNewTaskAjaxController::class . '::getTemplatesAction', + ], + 's7wizardGetGeneralFields' => [ + 'path' => '/s7wizard/getGeneralFields', + 'method' => ['GET'], + 'target' => CreateNewTaskAjaxController::class . '::getGeneralFieldsAction', + ], + 's7wizardGetEditorFields' => [ + 'path' => '/s7wizard/getEditorFields', + 'method' => ['GET'], + 'target' => CreateNewTaskAjaxController::class . '::getEditorFieldsAction', + ], +]; diff --git a/Configuration/Services.php b/Configuration/Services.php index c20c16b..74565bf 100644 --- a/Configuration/Services.php +++ b/Configuration/Services.php @@ -14,6 +14,7 @@ */ use SUDHAUS7\Sudhaus7Wizard\Cli\RunCommand; +use SUDHAUS7\Sudhaus7Wizard\Controller\Backend\CreateNewTaskAjaxController; use SUDHAUS7\Sudhaus7Wizard\EventHandlers\DefaultSiteSorterListener; use SUDHAUS7\Sudhaus7Wizard\EventHandlers\Extensions\TxNewsFixRecordHandler; use SUDHAUS7\Sudhaus7Wizard\EventHandlers\Extensions\TxNewsPluginHandlerEvent; @@ -38,6 +39,7 @@ __DIR__ . '/../Classes/Domain/Model/', __DIR__ . '/../Classes/Events/', __DIR__ . '/../Classes/Backend/', + __DIR__ . '/../Classes/ContextMenu/', ]); $services->alias(SourceInterface::class, LocalDatabase::class); @@ -48,6 +50,9 @@ 'schedulable' => true, ]); + $services->set(CreateNewTaskAjaxController::class) + ->public(); + $services->set(PreSysFileReferenceEventHandler::class) ->tag('event.listener', ['identifier' => 's7wizardBaseHandleSysFileReferences']); diff --git a/Resources/Private/Templates/.gitkeep b/Resources/Private/Templates/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/Resources/Private/Templates/Backend/EditorFields.html b/Resources/Private/Templates/Backend/EditorFields.html new file mode 100644 index 0000000..ebfe12a --- /dev/null +++ b/Resources/Private/Templates/Backend/EditorFields.html @@ -0,0 +1,28 @@ + +
+
+ + +
+
+ + +
+
+ + +
+
+ diff --git a/Resources/Private/Templates/Backend/GeneralFields.html b/Resources/Private/Templates/Backend/GeneralFields.html new file mode 100644 index 0000000..62eafe7 --- /dev/null +++ b/Resources/Private/Templates/Backend/GeneralFields.html @@ -0,0 +1,40 @@ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ diff --git a/Resources/Private/Templates/Backend/GetTemplates.html b/Resources/Private/Templates/Backend/GetTemplates.html new file mode 100644 index 0000000..f74252d --- /dev/null +++ b/Resources/Private/Templates/Backend/GetTemplates.html @@ -0,0 +1,28 @@ + +
+ + +
+ diff --git a/Resources/Private/Templates/Backend/TemplateFields.html b/Resources/Private/Templates/Backend/TemplateFields.html new file mode 100644 index 0000000..a462be4 --- /dev/null +++ b/Resources/Private/Templates/Backend/TemplateFields.html @@ -0,0 +1,28 @@ + +
+
+ + +
+
+ + +
+
+ + +
+
+ diff --git a/Resources/Public/JavaScript/CreateWizardTaskWizard.js b/Resources/Public/JavaScript/CreateWizardTaskWizard.js new file mode 100644 index 0000000..e699947 --- /dev/null +++ b/Resources/Public/JavaScript/CreateWizardTaskWizard.js @@ -0,0 +1,183 @@ +'use strict'; + +define([ + 'jquery', + 'TYPO3/CMS/Backend/Modal', + 'TYPO3/CMS/Backend/Severity', + 'TYPO3/CMS/Backend/MultiStepWizard', + 'TYPO3/CMS/Backend/Icons', + 'TYPO3/CMS/Backend/Notification', + 'TYPO3/CMS/Core/SecurityUtility', +], function($, Modal, Severity, MultiStepWizard, Icons, Notification) { + let CreateWizardTaskWizard = {}; + + CreateWizardTaskWizard.createTask = function (table, uid) { + let createTaskForm = new FormData(); + createTaskForm.set('wizard[sourcepid]', uid); + MultiStepWizard.addSlide( + 's7wizard-select-template', + 'Select template page', + '', + Severity.info, + 'Template', + slide => { + MultiStepWizard.lockNextStep(); + MultiStepWizard.lockPrevStep(); + MultiStepWizard.blurCancelStep(); + fetch(TYPO3.settings.ajaxUrls.s7wizardGetTemplates, { + method: 'GET', + credentials: 'same-origin' + }) + .then(response => response.text()) + .then(data => { + const response = JSON.parse(data); + if (response.success === false) { + Notification.error( + 'No templates found' + ); + return; + } + if (response.templates.length === 1) { + const selectedTemplate = response.templates.pop(); + createTaskForm.set('wizard[base]', selectedTemplate.value); + MultiStepWizard.unlockNextStep(); + MultiStepWizard.triggerStepButton('next'); + } + slide.html(response.html); + $('#selectTemplate', slide).on('change', () => { + if (this.value !== '') { + createTaskForm.set('wizard[base]', this.value); + MultiStepWizard.unlockNextStep(); + } + }) + }) + } + ).addSlide( + 's7wizard-general-settings', + 'Set general settings', + '', + Severity.info, + 'General', + slide => { + fetch(TYPO3.settings.ajaxUrls.s7wizardGetGeneralFields, { + method: 'GET', + credentials: 'same-origin' + }) + .then(response => response.text()) + .then(data => { + const response = JSON.parse(data); + slide.html(response.html); + let form = $('form#generalWizardForm', slide)[0]; + + $('input.form-control', slide).each(function () { + this.addEventListener('keyup', function () { + if (form.checkValidity()) { + $('input.form-control', slide).each(function () { + let formKey = 'address[' + this.name + ']'; + + createTaskForm.set(formKey, this.value); + }); + MultiStepWizard.unlockNextStep(); + } + }); + }); + }) + } + ).addSlide( + 's7wizard-editor-settings', + 'Set up new editor', + '', + Severity.info, + 'Editor', + slide => { + fetch(TYPO3.settings.ajaxUrls.s7wizardGetEditorFields, { + method: 'GET', + credentials: 'same-origin' + }) + .then(response => response.text()) + .then(data => { + const response = JSON.parse(data); + slide.html(response.html); + let form = $('form#editorWizardForm', slide)[0]; + + $('input.form-control', slide).each(function () { + this.addEventListener('keyup', function () { + if (form.checkValidity()) { + $('input.form-control', slide).each(function () { + let formKey = 'address[' + this.name + ']'; + + createTaskForm.set(formKey, this.value); + }); + MultiStepWizard.unlockNextStep(); + } + }); + }); + }) + } + ).addSlide( + 's7wizard-template-settings', + 'Set template settings', + '', + Severity.info, + 'Template', + slide => { + const templateSettingsUrl = TYPO3.settings.ajaxUrls.s7wizardGetTemplateSettings + '&template=' + createTaskForm.get('wizard[base]'); + fetch(templateSettingsUrl, { + method: 'GET', + credentials: 'same-origin' + }) + .then(response => response.text()) + .then(data => { + const response = JSON.parse(data); + slide.html(response.html); + let form = $('form#templateWizardForm', slide)[0]; + + $('input.form-control', slide).each(function () { + this.addEventListener('keyup', function () { + if (form.checkValidity()) { + $('input.form-control', slide).each(function () { + let formKey = 'address[' + this.name + ']'; + + createTaskForm.set(formKey, this.value); + }); + MultiStepWizard.unlockNextStep(); + } + }); + }); + }) + } + ).addFinalProcessingSlide(function () { + fetch(TYPO3.settings.ajaxUrls.wizardCreateNewTask, { + method: 'POST', + cache: 'no-cache', + body: createTaskForm, + }) + .then((response) => response.text()) + .then(function (text) { + const data = JSON.parse(text); + + if (data.success === false) { + Array.prototype.forEach.call( + data.error, + function (errorText) { + Notification.error('ERROR', errorText); + }, + ); + document.location = data.returnUrl; + } + + document.location = data.redirectUrl; + MultiStepWizard.dismiss(); + }); + }) + .done(function () { + MultiStepWizard.show(); + MultiStepWizard.getComponent().on('wizard-dismiss', function () { + // hard reset old wizard steps here + createTaskForm = new FormData(); + }); + }); + } + + return CreateWizardTaskWizard; +}); diff --git a/Resources/Public/Js/.gitkeep b/Resources/Public/Js/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/ext_localconf.php b/ext_localconf.php index d2cc618..45740c1 100644 --- a/ext_localconf.php +++ b/ext_localconf.php @@ -10,3 +10,7 @@ * * The TYPO3 project - inspiring people to share! */ +(static function (): void { + $GLOBALS['TYPO3_CONF_VARS']['BE']['ContextMenu']['ItemProviders'][1717198724657] = + \SUDHAUS7\Sudhaus7Wizard\ContextMenu\ItemProvider::class; +})();