Where can I set target step for failed purchase? - sylius

I am using custom checkout scenario which doesn't contain step "payment". However when purchase step fails, it tries to redirect to payment. Where can I change this behaviour?
Thanks.
Updated:
ChceckoutProcessScenario.php
class CheckoutProcessScenario implements ProcessScenarioInterface
{
public function build(ProcessBuilderInterface $builder)
{
$cart = $this->getCurrentCart();
$builder
->add('security', 'sylius_checkout_security')
->add('delivery', new Step\DeliveryStep())
->add('finalize', 'sylius_checkout_finalize')
->add('purchase', 'sylius_checkout_purchase')
;
$builder
->setDisplayRoute('sylius_checkout_display')
->setForwardRoute('sylius_checkout_forward')
->setRedirect('sylius_homepage')
->validate(function () use ($cart) {
return !$cart->isEmpty();
})
;
}
...
}
app/config/config.yml:
sylius.checkout.step.delivery.template: '#CoreBundle/Resources/views/Frontend/Checkout/Step/delivery.html.twig'
sylius.checkout_scenario.class: ZDG\CoreBundle\Checkout\CheckoutProcessScenario
and then there is checkoutStep, but those are only changed files.
I've looked into state-machine.yml, but it only defines states of order and payment, which i do not wish to modify.

So, the solution was les than expected: During pruchase step an event is triggered (sylius.checkout.purchase.complete), which has a listiner returning a response according to payment status. If status is not finished, it takes a parameter from container and redirects to provided url. This url is hardcoded in sylius/sylius/src/Sylius/Bundle/CoreBundle/Resources/config/services.xml:324

if you implemented custom class implementing ProcessScenarioInterface
There is also state machine configuration that could affect the redirection.
You will have to configure, what to do after each state change of the order.
Have a look in Sylius\CoreBundle\Resources\config\state-machine.yml
You can also check this for better understanding https://github.com/Sylius/Sylius/wiki/Status

Related

How to return a status code from an endpoint that can then be handled by app.UseStatusCodePages() middleware?

If I return StatusCode(403) or any other error code from an endpoint, any configuration of app.UseStatusCodePages<whatever> will be ignored.
I believe this is because the StatusCode(<whatever>) will automatically create a result object, and UseStatusCodePages only kicks in if there is an error status code and no content.
So how do I set a status code result in an IActionResult type endpoint and then return without setting any content so that UseStatusCodePages will handle the job of providing a suitable resonse?
As far as I know, the UseStatusCodePages will just be fired when the action result is the StatusCodeResult.
If you put some value inside the status codes, it will return the object result which will not trigger the UseStatusCodePages.
So I suggest you could directly use StatusCodeResult(403), then if you want to put some value to the StatusCodeResult, I suggest you could put it inside the httpcontext's item.
More details, you could refer to below codes:
public IActionResult OnGet()
{
HttpContext.Items.Add("test","1");
return StatusCode(403);
}
Program.cs:
app.UseStatusCodePages(async statusCodeContext =>
{
var status = statusCodeContext.HttpContext.Items["test"];
// using static System.Net.Mime.MediaTypeNames;
statusCodeContext.HttpContext.Response.ContentType = Text.Plain;
await statusCodeContext.HttpContext.Response.WriteAsync(
$"Status Code Page: {statusCodeContext.HttpContext.Response.StatusCode}");
});
Result:
The issue was that I have the ApiController attribute on the endpoint controller. One of the things this attribute does is to automatically create a ProblemDetails response body for any failed requests, and it is this that prevents UseStatusCodePages from having any effect.
The solution is to either remove the ApiController attribute if you do not require any of its features, or alternatively its behaviour of automatically creating ProblemDetails responses can be disabled using the following configuration in Program.cs (or Startup.cs in old style projects).
builder.Services.AddControllers().ConfigureApiBehaviorOptions(options =>
{
options.SuppressMapClientErrors = true;
});

Module Creation: post process problems

Goodmorning everyone,
I'm developing a module for prestashop 1.7, at the moment I'm having problems intercepting the postprocess method in the main class of my module.
I need to do the checks on submit the form (which are on the user profile page, where I set personal information).
From what I understand, in a form a submit is made, the first thing that is called in a class is precisely the postProcess () method that takes care of validating the data received from the form just submissive (correct me if I'm wrong).
The problem is that when I submit my form it does not enter the postPorcess () method (I checked for a die ("test") and it does not even show the latter), while if I do the check I need by invoking my method staff inside a hook is made,
Can you tell me where I'm wrong?
Thank you very much and have a nice day.
Daniel.
Daniel,
This might be an endpoint problem, however, if you are sure to just handle the request via this Class, just use Tools::getValue('something_in_form') / Tools::isSubmit('var') to check that it's sent.
You don't really need to apply this one. If you need example, you should check Prestashop's native modules or Admin controllers, as it depends a lot of where you need to do this.
My thought after some years of module dev is that you should use a module front controller endpoint as you would with an API and a do a response in JSON like this example :
<?php
class DummyModuleNameAjaxModuleFrontController extends ModuleFrontController
{
public function initContent()
{
$response = array();
require_once _PS_MODULE_DIR_.'dummymodulename/dummymodulename.php';
$mod = new dummymodulename;
if (Tools::isSubmit('action') && Tools::isSubmit('var') && Tools::getValue('var') == $mod->getSomethingForSecurity()) {
$context = Context::getContext();
$cart = $context->cart;
switch (Tools::getValue('action')) {
case 'dummy_action_name':
// Don't forget to type it with an INT or secure this entry with strip_tags
$my_var = Tools::getValue('var');
break;
default:
break;
}
}
echo Tools::jsonEncode($response);
die;
}
}

CakePHP 3: Accessing current user in model

Since i'm new to CakePHP, I have simple problems I cannot figure out.
I use CakePHP 3.4. I try to write a simple logger functionality. Every change applied to a record, I want to be logged to the ChangeLog model.
Using afterSave() event, I have following code:
public function afterSave($event, $entity, $options) {
$logTable = TableRegistry::get('ChangeLogs');
foreach ($entity->getDirty() as $key) {
if($key != 'modified') {
$record = $logTable->newEntity();
$record->previous_value = $entity->getOriginal($key);
$record->new_value = $entity[$key];
$record->table_name = 'Stars';
$record->column_name = $key;
$record->row_id = $entity->id;
$record->user_id = [what should i put here?]
$record->user_id = $_SESSION['Auth']['user']['id'];
$logTable->save($record);
}
}
It works well, but I also want to know which user performed operation and I don't know how can I obtain current user in the Model.
I try to avoid passing argument in controller, because I want user to be detected automaticly, and as a developer I don't want to remember about it every time I try change/add new functionalities in controller.
Do not fiddle with superglobals directly in CakePHP, this will surely bite you at some point, especially in the test environment! Always use the abstracted methods (like the session object) to access such data!
That being said, you could use events to inject the current user into the model callback/event flow. For example register globally to Model.afterSave, and pass the current user into the options.
Here's a basic example to demonstrate the principle. Imagine somthing like this in your app controller:
use Cake\Datasource\EntityInterface;
use Cake\Event\Event;
use Cake\Event\EventManager;
// ...
public function initialize()
{
parent::initialize();
// ...
EventManager::instance()->on(
'Model.afterSave',
['priority' => -1],
function (Event $event, EntityInterface $entity, \ArrayObject $options) {
// retrieve the user id from the auth component
$options['user_id'] = $this->Auth->user('id');
}
);
}
Given the priority of -1 (the default priority is 10) it will be invoked before the model callback for that event, so that in your table class you'll have access to user_id via the $options argument.
$record->user_id = $options['user_id'];
For something more reusable you'd probably use a custom listener class. Also check out events like Auth.afterIdentify, Model.initialize, and Controller.intialize/startup, these could be leaveraged to register your model events listener and to retrieve the current user.
See also
Awesome CakePHP > Auditing / Logging
Cookbook > Events System
Cookbook > Events System > Registering Listeners
Cookbook > Events System > Establishing Priorities
Cookbook > Database Access & ORM > Table Objects > Lifecycle Callbacks
Cookbook > Controllers > Request Life-cycle Callbacks
This solution seems to allow you to pass the logged in user into the model layer:
https://github.com/UseMuffin/Footprint
It is not hooked into the model layer through events like the solution above.

Laravel 5.2 : Login "attempt" Event Handling

In Laravel 5.2, i need to do something while the authentication process is being triggered. (Not "after" the login.)
Here are what i have done.
In EventServiceProvider.php:
protected $listen = [
'Illuminate\Auth\Events\Attempting' => ['App\Listeners\UserLoginAttempt#handle'],
];
In app/Listeners/UserLoginAttempt.php:
<?php
namespace App\Listeners;
use Illuminate\Http\Request;
use Illuminate\Auth\Events\Login;
use Carbon\Carbon;
use Auth;
class UserLoginAttempt
{
public function __construct()
{
}
public function handle($event)
{
dd(Auth::user());
exit;
}
}
This always returns null.
** In fact, i have one "custom" field in the Login form, of which i need to capture from the above Event Handler. (I need to check something before actual Login event is done.)
How do i capture the Auth::user() from this (Illuminate\Auth\Events\Attempting) Handler please?
(In other words) How do i talk with the Login Form from the (Illuminate\Auth\Events\Attempting) Handler?
OR ----- Is the Auth::user() object created ONLY AFTER the login is fully processed? Which means i can not get it while in "attempting" stage?
Because i need to capture the login elements first, and if it is something "unauthorized" (after some calculations done in Handler), then i need to terminate the login attempt and route back to the login form from here.. by carrying the Failing Message along.
Please let me know what i'm missing here. Thank you.
According to documentation you are right that Auth::user() object is only created when user is authenticated.
Inside the Illuminate\Auth\Events\Attempting listener;
you can capture the form field by
public function handle(Attempting $event)
{
//return dd($event); // will echo all fields
$event->credentials['email']; // capturing the email field
}
you can also use the User model and then access the user table in DB, to get / check a user field in DB...

Yii-rights params/data for bizrule

Scenerio:
Using Yii-rights + Yii-user module in my project. In Rights, I generated operations based on my controller action, under update I added a child UpdateOwn.
For UpdateOwn, the bizrule is suppose to be a simple comparison that the logged in user's ID is equal to $model->user_id field.
Problem:
I understand yii checkaccess allow you to pass in variables as parameters and comparing with your defined bizrule. But how does it work for Yii-rights module? How or what are the data/params passed in to be used in bizrule? How can I define or pass my own data/params?
Yii-rights is a wrapper for standart yii-rbac. In rights module you have web-interface for your RBAC. When you creating AuthItem (Operation in rights web interface) you can define your own bizrule.
Here is code for creating AuthItem:
$item = $this->_authorizer->createAuthItem($formModel->name, $type, $formModel->description, $formModel->bizRule, $formModel->data);
$item = $this->_authorizer->attachAuthItemBehavior($item);
_authorizer here is an example of RAuthorizer class. Then we go to RDbAuthManager, which extends CDbAuthManager, where we createAuthItem function:
public function createAuthItem($name,$type,$description='',$bizRule=null,$data=null)
{
$this->db->createCommand()
->insert($this->itemTable, array(
'name'=>$name,
'type'=>$type,
'description'=>$description,
'bizrule'=>$bizRule,
'data'=>serialize($data)
));
return new CAuthItem($this,$name,$type,$description,$bizRule,$data);
}
This is how created AuthItem, in rights. Personally i prefer to use web interface. It have alot of great fetures and much easier to handle then go to code each time.
Then when we perform checkAccess() on AuthItem we call execute bizRule:
public function executeBizRule($bizRule,$params,$data)
{
return $bizRule==='' || $bizRule===null || ($this->showErrors ? eval($bizRule)!=0 : #eval($bizRule)!=0);
}
This is how RBAC in yii work, and rights is just a cool wrapper for it. Rights doesn't change logic of how things must be done.
So in basic yii-rbac if you want to allow update only Own records you do:
$bizRule='return Yii::app()->user->id==$params["user"]->username;';
$task=$auth->createTask('updateOwnUser','update a your own account',$bizRule);
$task->addChild('updateUser');
Then you call it like this:
$user=$this->loadUser();
$params = array('user' => $user);
if(Yii::app()->user->checkAccess('updateOwnUser', $params){
..................
}
In rights it's already implemented with filters. Only thing what you need to do is add to your controller:
class MyController extends RController{
.............
public function filters()
{
return array(
'rights',
............
);
}
.............
}
So define your bizrule for item in web interface, change your controller code, and actually thats it. To know what variables to use in bizrule you can watch on RightsFilter.php code, where checkAccess() performed.
And on top of all of this i'll say about how checkAccess() does :
For each assigned auth item of the user, it first checks if the bizRule for the assignment returns true.
If true, it calls the item's checkAccess method. If the item's bizRule returns true,
2.1. If the item name is the same as the name passed in the original checkAccess() method, it returns true;
2.2. Otherwise, for every child item, it calls its checkAccess.
Hope this will clarify some aspects of RBAC and help in your task.
The yii-rights module has the following properties:
/**
* #property boolean whether to enable business rules.
*/
public $enableBizRule = true;
/**
* #property boolean whether to enable data for business rules.
*/
public $enableBizRuleData = false;
To set bizrule data via the web interface you have to set $enableBizRuleData = true in your application configuration.
Please note that the UI is limited and you can set data only for Auth-Items not for Auth-Assignments. Also the value for data has to be a serialized PHP variable.
As mentioned by #ineersa you can access $data in unserialized form in your bizRule.
It's also worth noting, that Yii checks first the bizRule for the Auth-Item and then additionally for the Auth-Assignment.
[edit] added example
Auth Item
bizRule
Check if the assignment has all the keys specified in the item data
return BizRule::compareKeys($params, $data, 'Editor');
data
a:1:{s:8:"language";b:1;}
Auth Assignment
Check if the application language matches the assignment data
bizRule
return BizRule::compareApplicationLanguage($params, $data);
data
a:1:{s:8:"language";s:5:"de_de";}
[edit] added code link
Here is the full Helper Code