TYPO3 tx_form conditional skip steps - forms

I using TYPO3 8 LTS and i want to extend the form.
Right now I am trying to add a condition in my form that skips all other steps and runs my finishers. In the documentation it is written that you have to use the afterInitializeCurrentPage function:
/**
* #param FormRuntime $formRuntime
* #param CompositeRenderableInterface $currentPage
* #param null|CompositeRenderableInterface $lastPage
* #param mixed $requestArguments submitted value of the element *before post processing*
* #return CompositeRenderableInterface
*/
public function afterInitializeCurrentPage(
FormRuntime $formRuntime,
CompositeRenderableInterface $currentPage,
CompositeRenderableInterface $lastPage = null,
array $requestArguments = []
): CompositeRenderableInterface {
if ($requestArguments['personalized'] === '0') {
// code here ...
}
return $currentPage;
}
My problem is i do not know how i execute the finishers out of this function..
i hope someone can give me a hint or something else..
[EDIT]
next problem is if i use the afterInitializeCurrentPage method i get an exception for other forms in my site:
Argument 2 passed to VENDOR\YourNamespace\YourClass::afterInitializeCurrentPage() must implement interface TYPO3\CMS\Form\Domain\Model\Renderable\CompositeRenderableInterface, null given, called in [..]/typo3/sysext/form/Classes/Domain/Runtime/FormRuntime.php on line 254
Many Thanks!

You can call the finisher Class Like below.
You need to add below line in ext_localconf.php file. like this
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/form']['afterInitializeCurrentPage'][]
= \VENDOR\YourNamespace\Hooks\YourClass::class;
After added this function like below on path Classes/Hooks/YourClass.php.
<?php
namespace \VENDOR\YourNamespace\Hooks;
class YourClass
{
/**
* #param \TYPO3\CMS\Form\Domain\Runtime\FormRuntime $formRuntime
* #param \TYPO3\CMS\Form\Domain\Model\Renderable\CompositeRenderableInterface $currentPage
* #param null|\TYPO3\CMS\Form\Domain\Model\Renderable\CompositeRenderableInterface $lastPage
* #param mixed $elementValue submitted value of the element *before post processing*
* #return \TYPO3\CMS\Form\Domain\Model\Renderable\CompositeRenderableInterface
*/
public function afterInitializeCurrentPage(\TYPO3\CMS\Form\Domain\Runtime\FormRuntime $formRuntime, \TYPO3\CMS\Form\Domain\Model\Renderable\CompositeRenderableInterface $currentPage, \TYPO3\CMS\Form\Domain\Model\Renderable\CompositeRenderableInterface $lastPage = null, array $requestArguments = []): CompositeRenderableInterface
{
return $currentPage;
}
}

Related

TYPO3 extension: Prevent record to be saved if the user enters the wrong value

I want to implement an eval in TYPO3 for a specific field in a TCA that prevents the record to be saved in the backend if the BE user enters a restricted value (like if doesn't enter anything or the input contains specific characters). So far i can only change the value in the eval function if it's not allowed, but i want the record NOT to be saved if the BE user clicks on 'save' while the field has restricted value. Is this even possible via an eval?
Edit: A cheap way to do this would be setting $value to NULL in the eval function if the input is of a restricted value, but that's definitely not the elegant way to do things as it throws an SQL error that might confuse the BE user.
So i basically need a way to prevent TYPO3 to persist the repository... Or to set the record back to the state it was in before the BE user clicked on 'save'...
Edit2: This is what i have... Nothing to exciting, just an IPv4 evaluation. But again, it changes only the value to something else, it DOESN'T prevent the creation or editing of the record if the input was not an IPv4.
<?php
namespace Cjk\Icingaconfgen\Evaluation;
use TYPO3\CMS\Core\Messaging\FlashMessage;
use TYPO3\CMS\Core\Messaging\FlashMessageService;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Class for field value validation/evaluation to be used in 'eval' of TCA
*/
class IPv4Evaluation
{
/**
* #param string $value
* #param string $is_in
* #param bool $set
* #return string
*/
public function evaluateFieldValue($value, $is_in, &$set)
{
if (!filter_var($value, FILTER_VALIDATE_IP)){
$value = 'Fehlerhafte Eingabe (IPv4)';
/** #var FlashMessage $message */
$message = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Messaging\\FlashMessage',
'Fehlerhafte Eingabe: .conf Datei wird nicht erstellt/editiert. Neue services können nicht hinzugefügt oder editiert werden.',
\TYPO3\CMS\Core\Messaging\FlashMessage::ERROR,
TRUE
);
/** #var $flashMessageService FlashMessageService */
$flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
$flashMessageService->getMessageQueueByIdentifier()->enqueue($message);
}
return $value;
}
}
So basically, and correct me if i'm wrong, the eval happens BETWEEN the input of the BE user (right after he clicks on the save button) and the persisting of the record. So there has to be a way to prevent the persisting of the new data, not only to change it at will.
I hope this makes my question more clear now, i don't know what else to write to explain it.
I show you a example of a validation in TCA. If validation failed record can't saved.
File myextension/Classes/Validation/Validator/MinReferencesValidator.php
<?php
namespace Vendor\Myextension\Validation\Validator;
/**
* Validator for min references
*/
class MinReferencesValidator extends \TYPO3\CMS\Extbase\Validation\Validator\AbstractValidator {
/**
* #var array
*/
protected $supportedOptions = [
'min' => [0, 'The minimum references to accept', 'integer'],
];
/**
* The given value is valid if it contains more then min items
*
* #param mixed $value The value that should be validated
* #return void
*/
public function isValid($value) {
if (!is_object($value)) {
$this->addError(
$this->translateErrorMessage(
'LLL:EXT:myextension/Resources/Private/Language/locallang.xlf:validator_object_notvalid',
'myextension'
), 1489870657);
return;
}
$minimum = $this->options['min'];
$countItems = count($value);
if ($countItems < $minimum) {
$this->addError(
$this->translateErrorMessage(
'LLL:EXT: myextension/Resources/Private/Language/locallang.xlf:validator_min_references',
'myextension',
[
$minimum
]
), 1489872300, [$minimum]);
return;
}
}
}
File myextension/Classes/Domain/Model/Youritem.php
/**
* Image of supplier (image reference)
*
* #var \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\TYPO3\CMS\Extbase\Domain\Model\FileReference>
* #validate NotEmpty, \Vendor\Yourextension\Validation\Validator\MinReferencesValidator(min=1)
*/
protected $images;

Invalid text representation: 7 ERROR: invalid input syntax for uuid: "test"

I'm using Symfony 3.2 with doctrine and postgresql.
I've created an entity with a uuid as primary key.
My entity definition:
/**
* Booking
*
* #ORM\Table(name="booking")
* #ORM\Entity(repositoryClass="AppBundle\Repository\BookingRepository")
* #ORM\EntityListeners({"AppBundle\EventListener\BookingListener"})
*/
class Booking {
/**
* #var string
*
* #ORM\Column(type="guid")
* #ORM\Id
* #ORM\GeneratedValue(strategy="UUID")
*/
private $id;
}
In my controller I have a show action like this:
/**
* #Route("booking/{id}", name="booking_show")
* #Method({"GET"})
*/
public function showAction(Request $request, Booking $booking) {
...
}
Everything seems to work fine, but when I try to load a route putting an wrong value as an ID (i.e. /booking/hello123), I receive a:
SQLSTATE[22P02]: Invalid text representation: 7 ERROR: invalid input syntax for uuid: "hello123"
Instead I would expect a 404.
Is there a way to capture this exception and redirect to a 404 page?
You can make use of Route Requirements - you can specify what conditions your parameter need to match to "qualify" to a certain route. This requirement is a regex, so all you need to do is to write a regex for an UUID
/**
* #Route("booking/{id}", name="booking_show", requirements={"id": "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"})
* #Method({"GET"})
*/
public function showAction(Request $request, Booking $booking) {
...
}
NOTE: Regex used above is just first result I found in Google for UUID regex, I didn't verify if it works
In the end if your id does not match regex, it does not match route and you should get 404.
you need to change the showaction
/**
* #Route("booking/{id}", name="booking_show")
* #Method({"GET"})
*/
public function showAction(Request $request,$bookingID) {
$em = $this->getDoctrine()->getManager();
$booking = $em->getRepository('AppBundle:Entity')->find(bookingID);
if(!$booking)
$this->createNotFoundException('No entity found with id :'.$bookingID);
...
}

Adding customer_balance_amount to Magento 2's Order REST API

By default Magento's Order REST API doesn't submit the store credit amount used for the order(customer_balance_amount col in the DB). I need to expose that to the API interface, but as of now am unable to. I tried two approaches:
http://magehit.com/blog/how-to-get-value-of-custom-attribute-on-magento-2-rest-api/ - using an observer, but that doesn't seem to have any reflection on the API data
and
http://www.ipragmatech.com/extend-magento2-rest-api-easy-steps/ - which I successfully tried, but it concerns actually creating a new ednpoint instead of overriding/extending the current API.
I was actually able to reproduce that by directly altering the OrderInterface and Order model inside the module-sales core module, but I want to achieve that the "proper" way instead of modifying core.
I would be thankful if anyone shares some knowledge how to do that.
Edit: adding the code that made the solution working, but the goal is to make it the proper way, not edit the core files like so:
vendor/magento/module-sales/Api/Data/OrderInterface.php:
/*
* Customer Balance Amount
*/
const CUSTOMER_BALANCE_AMOUNT = 'customer_balance_amount';
/**
* Returns customer_balance_amount
*
* #return float Customer Balance Amount
*/
public function getCustomerBalanceAmount();
/**
* Sets the customer_balance_amount for the order.
*
* #param float $amount
* #return $this
*/
public function setCustomerBalanceAmount($amount);
vendor/magento/module-sales/model/Order.php:
/**
* Returns customer_balance_amount
*
* #return float
*/
public function getCustomerBalanceAmount()
{
return $this->getData(OrderInterface::CUSTOMER_BALANCE_AMOUNT);
}
/**
* Sets the customer_balance_amount for the order.
*
* #param float $amount
* #return $this
*/
public function setCustomerBalanceAmount($amount)
{
return $this->setData(OrderInterface::CUSTOMER_BALANCE_AMOUNT, $amount);
}
Regards,
Alex
It looks like this is actually a bug, since Magento does define the balance columns as extension attributes in vendor/magento/module-customer-balance/etc/extension_attributes.xml
Looking at the GiftMessage module, the way to do this is via a plugin.
vendor/magento/module-gift-message/etc/di.xml
<type name="Magento\Sales\Api\OrderRepositoryInterface">
<plugin name="save_gift_message" type="Magento\GiftMessage\Model\Plugin\OrderSave"/>
<plugin name="get_gift_message" type="Magento\GiftMessage\Model\Plugin\OrderGet"/>
</type>
\Magento\GiftMessage\Model\Plugin\OrderGet
/**
* Get gift message for order
*
* #param \Magento\Sales\Api\Data\OrderInterface $order
* #return \Magento\Sales\Api\Data\OrderInterface
*/
protected function getOrderGiftMessage(\Magento\Sales\Api\Data\OrderInterface $order)
{
$extensionAttributes = $order->getExtensionAttributes();
if ($extensionAttributes && $extensionAttributes->getGiftMessage()) {
return $order;
}
try {
/** #var \Magento\GiftMessage\Api\Data\MessageInterface $giftMessage */
$giftMessage = $this->giftMessageOrderRepository->get($order->getEntityId());
} catch (NoSuchEntityException $e) {
return $order;
}
/** #var \Magento\Sales\Api\Data\OrderExtension $orderExtension */
$orderExtension = $extensionAttributes ? $extensionAttributes : $this->orderExtensionFactory->create();
$orderExtension->setGiftMessage($giftMessage);
$order->setExtensionAttributes($orderExtension);
return $order;
}

JSDoc Class suggested layout

I'm brand new to JSDoc, and I'm trying to figure out the best way to tag my code. For some reason after I label something as a #class, I can't get anything to appear as #inner:
/**
* The logger, to simply output logs to the console (or potentially a variable)
* #class Logger
* #requires 'config/globalConfig'
*/
define(["config/globalConfig"], function (config) {
/**
* Instantiate a new copy of the logger for a class/object
* #constructor
* #param name {string} - The name of the class instantiating the logger
*/
return function (name) {
/**
* #memberOf Logger
* #type {string}
*/
this.name = name;
/**
* Place the message on the console, only for "DEV" level debugging
* #function
* #memberOf Logger
* #param message {string}
*/
this.debug = function (message) {
... some code ...
};
};
});
Right now all the members are appearing as <static>. If I add the #inner tag to any of the attributes/functions they vanish completely from the output.
Edit: I also forgot to mention. The #constructor flag doesn't seem to be working either. If I remove that entire section, it appears the same in the output. The output does not include the #param that I would like to mention with my constructor.
Please let me know if this is completely off, I'm just kind of guessing here since the JSDoc3 documentation is a bit difficult to read.
So I figured it out, still not sure if it's absolutely correct. I had to use "Logger~name" to have it appear correctly as an inner function. According to the JSDoc documentation, this ~ is "rarely used". Seems to be the only thing that works for me.
define(["config/globalConfig"], function (config) {
/**
* The logger, to simply output logs to the console (or potentially a variable)
* #class Logger
* #param name {string} - The name of the class instantiating the logger
*/
return function (name) {
this.name = name;
/**
* Place the message on the console, only for "DEV" level debugging
* #function Logger~debug
* #param message {string}
*/
this.debug = function (message) {
... code ...
};
};
});

An error occurred while trying to call Controller->createAction()

I am trying to create something with extbase, but the error-message I get is not very helpful. I took the blog_example extension as a guide. A (maybe) important difference is: I don't have a database table because I want to write a custom domain repository that connects to an external servive through REST.
The actual error message (displayed above the plugin, not as an exception message):
An error occurred while trying to call Tx_MyExt_Controller_SubscriptionController->createAction()
Classes/Controller/SubscriptionController:
Stripped down to the important parts.
class Tx_MyExt_Controller_SubscriptionController extends Tx_Extbase_MVC_Controller_ActionController
{
/**
* #var Tx_MyExt_Domain_Repository_SubscriberRepository
*/
protected $subscriberRepository;
/**
* #return void
*/
public function initializeAction()
{
$this->subscriberRepository = t3lib_div::makeInstance('Tx_MyExt_Domain_Repository_SubscriberRepository');
}
/**
* #param Tx_MyExt_Domain_Model_Subscriber $subscriber
* #dontvalidate $subscriber
* #return string The rendered view
*/
public function newAction(Tx_MyExt_Domain_Model_Subscriber $subscriber = null)
{
$this->view->assign('subscriber', $subscriber);
}
/**
* #param Tx_MyExt_Domain_Model_Subscriber $subscriber
* #return string The rendered view
*/
public function createAction(Tx_MyExt_Domain_Model_Subscriber $subscriber)
{ }
}
Classes/Domain/Model/Subscriber
class Tx_MyExt_Domain_Model_Subscriber extends Tx_Extbase_DomainObject_AbstractEntity
{
/**
* #var string
* #dontvalidate
*/
protected $email = '';
/**
* #param string $email
* #return void
*/
public function setEmail($email)
{
$this->email = $email;
}
/**
* #return string
*/
public function getEmail()
{
return $this->email;
}
}
Resources/Private/Templates/Subscription/new
<f:form action="create" controller="Subscription" objectName="Subscriber" object="{subscriber}" method="post">
<f:form.textfield property="email"></f:form.textfield>
<f:form.submit value="submit"></f:form.submit>
</f:form>
Facts
Adding $subscriber = null removes the message. But $subscriber is null then
A var_dump($this->request->getArguments()); displays the form's fields
There is an index action, and it is also the first action defined in ext_localconf.php
The hints and solutions I found aren't working for me, so I hope someone can guide me into the right direction.
I've got the same bug.
If you pass an Model as argument to an method, it will also validate the model fields.
I've had this annotation on my model property:
/**
*
* #var \string
* #validate NotEmpty
*/
It validates the "#validate" annotation.
The field in the database was empty so i got the error message
An error occurred while trying to call ...
It would be good if there was a better error message.
You need to customize the validation annotation or verify that the property is not empty in the database
Hope it helps somebody
In addtion: check any Validations in your Model and your TCA. If a field is marked as #validate NotEmpty in your Model and is not marked appropriately in the TCA, a record can be saved ignoring the #validate settings in the Model. This can happen if you change the Model and/or TCA after creating records.
An example:
Field 'textfield' is set to not validate, both in the TCA and the Model. You create a new record and save it without filling in the field 'textfield' (you can, it is not set to validate). You then change the Model setting 'textfield' to #validate NotEmpty and then try to show the record on the FE, you will get the error.
The solution for that example:
Simply remove the validation in your Model OR check validations in the TCA and Model so that they work together.
--
A German blog post covers this solution: http://www.constantinmedia.com/2014/04/typo3-extbase-an-error-occurred-while-trying-to-call-anyaction/
just override the template method getErrorFlashMessage in yout controller to provide a custom error message...
/**
* A template method for displaying custom error flash messages, or to
* display no flash message at all on errors. Override this to customize
* the flash message in your action controller.
*
* #return string|boolean The flash message or FALSE if no flash message should be set
* #api
*/
protected function getErrorFlashMessage() {
return 'An error occurred while trying to call ' . get_class($this) . '->' . $this->actionMethodName . '()';
}
classic case of "start over from scratch and it works, and if you compare it you have the same code, though".
I updated the code in the question, maybe it helps someone.