diff --git a/typo3/sysext/form/Classes/Hooks/DataStructureIdentifierHook.php b/typo3/sysext/form/Classes/Hooks/DataStructureIdentifierHook.php index 13436ba1711522ad0dea65b96d55f8dc131c96bd..cf284ea7c1a1b7132955b960fe4ac3dbd1fa172d 100644 --- a/typo3/sysext/form/Classes/Hooks/DataStructureIdentifierHook.php +++ b/typo3/sysext/form/Classes/Hooks/DataStructureIdentifierHook.php @@ -110,10 +110,19 @@ class DataStructureIdentifierHook $formIsAccessible = true; } - $dataStructure['sheets']['sDEF']['ROOT']['el']['settings.persistenceIdentifier']['TCEforms']['config']['items'][] = [ - $form['name'] . ' (' . $form['persistenceIdentifier'] . ')', - $form['persistenceIdentifier'], - ]; + if ($form['invalid']) { + $dataStructure['sheets']['sDEF']['ROOT']['el']['settings.persistenceIdentifier']['TCEforms']['config']['items'][] = [ + $form['name'] . ' (' . $form['persistenceIdentifier'] . ')', + $form['persistenceIdentifier'], + 'overlay-missing' + ]; + } else { + $dataStructure['sheets']['sDEF']['ROOT']['el']['settings.persistenceIdentifier']['TCEforms']['config']['items'][] = [ + $form['name'] . ' (' . $form['persistenceIdentifier'] . ')', + $form['persistenceIdentifier'], + 'content-form' + ]; + } } if (!empty($identifier['ext-form-persistenceIdentifier']) && !$formIsAccessible) { diff --git a/typo3/sysext/form/Classes/Mvc/Configuration/YamlSource.php b/typo3/sysext/form/Classes/Mvc/Configuration/YamlSource.php index d342c544185038f3eff04695e933a6700051f875..a82c83dcf2e84e7e5b98502504e62d362e854ef2 100644 --- a/typo3/sysext/form/Classes/Mvc/Configuration/YamlSource.php +++ b/typo3/sysext/form/Classes/Mvc/Configuration/YamlSource.php @@ -107,7 +107,7 @@ class YamlSource } } catch (ParseException $exception) { throw new ParseErrorException( - 'A parse error occurred while parsing file "' . $fileIdentifier . '". Error message: ' . $exception->getMessage(), + 'An error occurred while parsing file "' . $fileIdentifier . '": ' . $exception->getMessage(), 1480195405 ); } diff --git a/typo3/sysext/form/Classes/Mvc/Persistence/FormPersistenceManager.php b/typo3/sysext/form/Classes/Mvc/Persistence/FormPersistenceManager.php index fca17892f828c7ed38940a68885d894455662d84..64eef0172e78357d765f761c16b6cfed625d2308 100644 --- a/typo3/sysext/form/Classes/Mvc/Persistence/FormPersistenceManager.php +++ b/typo3/sysext/form/Classes/Mvc/Persistence/FormPersistenceManager.php @@ -105,7 +105,22 @@ class FormPersistenceManager implements FormPersistenceManagerInterface } else { $file = $this->getFileByIdentifier($persistenceIdentifier); } - return $this->yamlSource->load([$file]); + + try { + $yaml = $this->yamlSource->load([$file]); + } catch (\Exception $e) { + $yaml = [ + 'identifier' => $file->getCombinedIdentifier(), + 'label' => $e->getMessage(), + 'error' => [ + 'code' => $e->getCode(), + 'message' => $e->getMessage() + ], + 'invalid' => true, + ]; + } + + return $yaml; } /** @@ -237,6 +252,8 @@ class FormPersistenceManager implements FormPersistenceManagerInterface 'removable' => true, 'location' => 'storage', 'duplicateIdentifier' => false, + 'invalid' => $form['invalid'], + 'error' => $form['error'], ]; $identifiers[$form['identifier']]++; } @@ -258,6 +275,8 @@ class FormPersistenceManager implements FormPersistenceManagerInterface 'removable' => $this->formSettings['persistenceManager']['allowDeleteFromExtensionPaths'] ? true: false, 'location' => 'extension', 'duplicateIdentifier' => false, + 'invalid' => $form['invalid'], + 'error' => $form['error'], ]; $identifiers[$form['identifier']]++; } diff --git a/typo3/sysext/form/Resources/Private/Backend/Templates/FormManager/Index.html b/typo3/sysext/form/Resources/Private/Backend/Templates/FormManager/Index.html index 26aec41d7b68d6120fb574b3bbda99f9fdf2dac8..e59648edef54d8994aed1f5eb72be3040ef15288 100644 --- a/typo3/sysext/form/Resources/Private/Backend/Templates/FormManager/Index.html +++ b/typo3/sysext/form/Resources/Private/Backend/Templates/FormManager/Index.html @@ -33,12 +33,17 @@ <f:for each="{forms}" as="form"> <tr> <td class="col-icon nowrap"> - <f:if condition="{form.duplicateIdentifier}"> + <f:if condition="{form.invalid}"> <f:then> - <span title="{f:translate(key: 'LLL:EXT:form/Resources/Private/Language/Database.xlf:formManager.duplicate_identifier')} {form.identifier}" data-toggle="tooltip" data-placement="top"> + <span title="{f:translate(key: 'LLL:EXT:form/Resources/Private/Language/Database.xlf:formManager.invalid')}" data-toggle="tooltip" data-placement="top"> <core:icon identifier="overlay-missing" /> </span> </f:then> + <f:else if="{form.duplicateIdentifier}"> + <span title="{f:translate(key: 'LLL:EXT:form/Resources/Private/Language/Database.xlf:formManager.duplicate_identifier')} {form.identifier}" data-toggle="tooltip" data-placement="top"> + <core:icon identifier="overlay-missing" /> + </span> + </f:else> <f:else> <span title="id={form.identifier}" data-toggle="tooltip" data-placement="right"> <core:icon identifier="content-elements-mailform" /> @@ -47,9 +52,9 @@ </f:if> </td> <td class="col-title col-responsive nowrap"> - <f:if condition="{form.readOnly}"> + <f:if condition="{form.invalid} || {form.readOnly}"> <f:then> - <div>{form.name}</div> + <div title="{form.name}">{form.name}</div> </f:then> <f:else> <f:link.action controller="FormEditor" action="index" arguments="{formPersistenceIdentifier: form.persistenceIdentifier}" title="{f:translate(key: 'LLL:EXT:form/Resources/Private/Language/Database.xlf:formManager.edit_form')}" data="{toggle: 'tooltip', placement: 'right'}">{form.name}</f:link.action> @@ -58,7 +63,7 @@ </td> <td class="col-control nowrap"> <div class="btn-group" role="group"> - <f:if condition="{form.readOnly}"> + <f:if condition="{form.invalid} || {form.readOnly}"> <f:then> <button class="btn btn-default form-record-readonly" disabled="disabled" title="{f:translate(key: 'LLL:EXT:form/Resources/Private/Language/Database.xlf:formManager.edit_form_not_allowed')}"><core:icon identifier="actions-open" /></button> </f:then> @@ -66,13 +71,21 @@ <f:link.action controller="FormEditor" action="index" arguments="{formPersistenceIdentifier: form.persistenceIdentifier}" title="{f:translate(key: 'LLL:EXT:form/Resources/Private/Language/Database.xlf:formManager.edit_form')}" class="btn btn-default form-record-open"><core:icon identifier="actions-open" /></f:link.action> </f:else> </f:if> - <a href="#" data-identifier="duplicateForm" data-form-persistence-identifier="{form.persistenceIdentifier}" data-form-name="{form.name}" title="{f:translate(key: 'LLL:EXT:form/Resources/Private/Language/Database.xlf:formManager.duplicate_this_form')}" class="btn btn-default form-record-duplicate"><core:icon identifier="t3-form-icon-duplicate" /></a> - <f:if condition="{form.removable}"> + <f:if condition="{form.invalid}"> <f:then> - <a href="#" data-identifier="removeForm" data-form-persistence-identifier="{form.persistenceIdentifier}" title="{f:translate(key: 'LLL:EXT:form/Resources/Private/Language/Database.xlf:formManager.delete_form')}" class="btn btn-default form-record-delete"><core:icon identifier="actions-edit-delete" /></a> + <button class="btn btn-default form-record-readonly" disabled="disabled" title="{f:translate(key: 'LLL:EXT:form/Resources/Private/Language/Database.xlf:formManager.duplicate_form_not_allowed')}"><core:icon identifier="t3-form-icon-duplicate" /></button> + <button class="btn btn-default form-record-readonly" disabled="disabled" title="{f:translate(key: 'LLL:EXT:form/Resources/Private/Language/Database.xlf:formManager.delete_form_not_allowed')}"><core:icon identifier="actions-edit-delete" /></button> </f:then> <f:else> - <button class="btn btn-default form-record-delete" disabled="disabled" title="{f:translate(key: 'LLL:EXT:form/Resources/Private/Language/Database.xlf:formManager.delete_form_not_allowed')}"><core:icon identifier="actions-edit-delete" /></button> + <a href="#" data-identifier="duplicateForm" data-form-persistence-identifier="{form.persistenceIdentifier}" data-form-name="{form.name}" title="{f:translate(key: 'LLL:EXT:form/Resources/Private/Language/Database.xlf:formManager.duplicate_this_form')}" class="btn btn-default form-record-duplicate"><core:icon identifier="t3-form-icon-duplicate" /></a> + <f:if condition="{form.removable}"> + <f:then> + <a href="#" data-identifier="removeForm" data-form-persistence-identifier="{form.persistenceIdentifier}" title="{f:translate(key: 'LLL:EXT:form/Resources/Private/Language/Database.xlf:formManager.delete_form')}" class="btn btn-default form-record-delete"><core:icon identifier="actions-edit-delete" /></a> + </f:then> + <f:else> + <button class="btn btn-default form-record-delete" disabled="disabled" title="{f:translate(key: 'LLL:EXT:form/Resources/Private/Language/Database.xlf:formManager.delete_form_not_allowed')}"><core:icon identifier="actions-edit-delete" /></button> + </f:else> + </f:if> </f:else> </f:if> </div> diff --git a/typo3/sysext/form/Resources/Private/Language/Database.xlf b/typo3/sysext/form/Resources/Private/Language/Database.xlf index 34651b1e12bd659dace3053798436d66c65e38c0..92877d9f1626faa28b53eb97d856e818f2a79c27 100644 --- a/typo3/sysext/form/Resources/Private/Language/Database.xlf +++ b/typo3/sysext/form/Resources/Private/Language/Database.xlf @@ -178,9 +178,15 @@ <trans-unit id="formManager.edit_form_not_allowed" xml:space="preserve"> <source>Edit this form is not allowed.</source> </trans-unit> + <trans-unit id="formManager.duplicate_form_not_allowed" xml:space="preserve"> + <source>Duplicate this form is not allowed.</source> + </trans-unit> <trans-unit id="formManager.delete_form_not_allowed" xml:space="preserve"> <source>Delete this form is not allowed.</source> </trans-unit> + <trans-unit id="formManager.invalid" xml:space="preserve"> + <source>Invalid YAML file!</source> + </trans-unit> <trans-unit id="formManager.duplicate_identifier" xml:space="preserve"> <source>Duplicate identifier!</source> </trans-unit> diff --git a/typo3/sysext/form/Tests/Unit/Hooks/DataStructureIdentifierHookTest.php b/typo3/sysext/form/Tests/Unit/Hooks/DataStructureIdentifierHookTest.php index 7ac93697141774df2e28b05e7c59d286bb102a46..74bc44f95f4a69fb9668b52a30496a8f5eb46643 100644 --- a/typo3/sysext/form/Tests/Unit/Hooks/DataStructureIdentifierHookTest.php +++ b/typo3/sysext/form/Tests/Unit/Hooks/DataStructureIdentifierHookTest.php @@ -171,8 +171,12 @@ class DataStructureIdentifierHookTest extends \TYPO3\TestingFramework\Core\Unit\ /** * @test + * @dataProvider parseDataStructureByIdentifierPostProcessDataProvider + * + * @param array $formDefinition + * @param array $expectedItem */ - public function parseDataStructureByIdentifierPostProcessAddsExistingFormItems() + public function parseDataStructureByIdentifierPostProcessAddsExistingFormItems(array $formDefinition, array $expectedItem) { $objectMangerProphecy = $this->prophesize(ObjectManager::class); GeneralUtility::setSingletonInstance(ObjectManager::class, $objectMangerProphecy->reveal()); @@ -180,17 +184,7 @@ class DataStructureIdentifierHookTest extends \TYPO3\TestingFramework\Core\Unit\ $objectMangerProphecy->get(FormPersistenceManagerInterface::class) ->willReturn($formPersistenceManagerProphecy->reveal()); - $existingForms = [ - [ - 'persistenceIdentifier' => 'hugo1', - 'name' => 'myHugo1', - ], - [ - 'persistenceIdentifier' => 'hugo2', - 'name' => 'myHugo2', - ] - ]; - $formPersistenceManagerProphecy->listForms()->shouldBeCalled()->willReturn($existingForms); + $formPersistenceManagerProphecy->listForms()->shouldBeCalled()->willReturn([$formDefinition]); $incomingDataStructure = [ 'sheets' => [ @@ -228,14 +222,7 @@ class DataStructureIdentifierHookTest extends \TYPO3\TestingFramework\Core\Unit\ 0 => 'default, no value', 1 => '', ], - 1 => [ - 0 => 'myHugo1 (hugo1)', - 1 => 'hugo1', - ], - 2 => [ - 0 => 'myHugo2 (hugo2)', - 1 => 'hugo2', - ], + 1 => $expectedItem, ], ], ], @@ -254,6 +241,39 @@ class DataStructureIdentifierHookTest extends \TYPO3\TestingFramework\Core\Unit\ $this->assertEquals($expected, $result); } + /** + * @return array + */ + public function parseDataStructureByIdentifierPostProcessDataProvider(): array + { + return [ + 'simple' => [ + [ + 'persistenceIdentifier' => 'hugo1', + 'name' => 'myHugo1', + ], + [ + 'myHugo1 (hugo1)', + 'hugo1', + 'content-form', + ], + ], + 'invalid' => [ + [ + 'persistenceIdentifier' => 'Error.yaml', + 'label' => 'Test Error Label', + 'name' => 'Test Error Name', + 'invalid' => true, + ], + [ + 'Test Error Name (Error.yaml)', + 'Error.yaml', + 'overlay-missing', + ], + ], + ]; + } + /** * Data provider for implodeArrayKeysReturnsString *