Drupal form error redirect - forms

I need help for this problem:
In my Drupal 7 site i need to perform a redirect to a certain page if the login form is invalid, only in this occasion, and only for login form.
What is the best way to do this? what function is called?
Thanks in advance.

You have to use hook_form_alter and add or alter the #validate options. Prefer the second method:
function MYMODULE_form_alter(&$form, $form_state, $form_id) {
if ($form_id == 'user_login') {
$form['#validate'][] = 'MYMODULE_user_login_form_validate';
}
}
function MYMODULE_user_login_form_validate($form, &$form_state) {
// Do some validation here. Better take the same codes from the validation functions below.
if (...) {
drupal_goto('CUSTOM_PATH');
}
}
You can find the validation functions inside ROOT/modules/user/user.module file line 2113:
function user_login_default_validators() {
return array('user_login_name_validate', 'user_login_authenticate_validate', 'user_login_final_validate');
}
Similar questions:
(6.x, although it is the same) Drupal Modify Login System
https://drupal.stackexchange.com/questions/22952/custom-validation-for-a-form

using hook_user_login method you can achieve the same,
/**
* Implements hook_user_login();
*/
function MODULE_NAME_user_login(&$edit, $account) {
$url = 'PAGE_TO_BE REDIRECTED';
if (isset($user->uid) && $user->uid > 0) {
drupal_goto($url);
}
}

Related

Form redirect for confirmation

Form redirect for confirmation can be currently managed using one of these two options:
1/ Flash message: using flashbag on the form page or another page like this:
$this->addFlash('success', 'Thank you');
return $this->redirectToRoute('confirmation_page');
2/ Confirmation page: using a dedicated confirmation like this:
return $this->redirectToRoute('confirmation_page');
BUT using option 2 makes the confirmation_page directly accessible from the browser without having submitted the form before. I am currently using flashbag mechanism to fix it by adding a $this->addFlash('success', true); before the redirection in the form and then checking the flashbag content in the confirmation page so that the route is accessible only once after being successfully redirected from the form.
Is there any best practice or more appropriate way to manage it?
/**
* #Route("/confirmation", methods="GET", name="confirmation_page")
*/
public function confirmation(): Response
{
$flashbag = $this->get('session')->getFlashBag();
$success = $flashbag->get("success");
if (!$success) {
return $this->redirectToRoute('app_home');
}
return $this->render('templates/confirmation.html.twig');
}
Flash Message is designed to display messages. Instead, use sessions in your application.
When submitting the confirmation form, create a variable in the session before the redirect
$this->requestStack->getSession()->set('verifyed',true);
return $this->redirectToRoute('confirmation_page');
Use the created variable in your method
public function confirmation(): Response
{
if (!$this->requestStack->getSession()->get('verifyed')) {
return $this->redirectToRoute('app_home');
}
return $this->render('templates/confirmation.html.twig');
}
Don't forget to inject the RequestStack into your controller
private RequestStack $requestStack;
public function __construct(RequestStack $requestStack)
{
$this->requestStack = $requestStack;
}

yii2 binds redirect to each link after first request

public function actionDone($id)
{
if ($model = $this->findModel($id)) {
$model["status"] = 3;
if ($model->save()) {
return $this->redirect(['test/index']);
}
} else {
throw new NotFoundHttpException('The requested page does not exist.');
}
}
It works only for the first time for each link. After that its just redirects to the 'test/index' without doing anything. Seems like browser (or smth else) remember, that if we open, for example, page site.com/?r=test/done&id=2 it should redirect to 'test/index' anyway.
Why is that? How can I fix it?
I even tried put die(); in the beginning of the method - anyway it redirects to 'test/index' until I use different link with another ID.
Thanks!

PHPUnit: Testing form submissions with session variables stored in Symfony2

I had a small test done in PHP for a Controller I had written in Symfony2:
class DepositControllerTest extends WebTestCase {
public function testDepositSucceeds() {
$this->crawler = self::$client->request(
'POST',
'/deposit',
array( "amount" => 23),
array(),
array()
);
$this->assertEquals(
"Deposit Confirmation",
$this->crawler->filter("title")->text());
}
}
Up to here, everything was great. Problem started when I realized I wanted to disable possible re-submissions while refreshing the page. So I added a small mechanism to send nonce on every submission.
It works something like this:
class ReplayManager {
public function getNonce() {
$uid = $this->getRandomUID();
$this->session->set("nonce", $uid);
return $uid;
}
public function checkNonce($cnonce) {
$nonce = $this->session->get("nonce");
if ($cnonce !== $nonce)
return false;
$this->session->set("nonce", null);
return true;
}
}
So I had to mofidy the controller to get the nonce when displaying the form, and consume it when submitting.
But now this introduces a problem. I cant make a request to POST /deposit because I dont know what nonce to send. I thought to requesting first GET /deposit to render the form, and setting one, to use it in the POST, but I suspect Symfony2 sessions are not working in PHPUnit.
How could I solve this issue? I would not want to go to Selenium tests, since they are significant slower, not to mention that I would have to rewrite A LOT of tests.
UPDATE: I add a very simplified version of the controller code by request.
class DepositController extends Controller{
public function formAction(Request $request){
$this->replayManager = $this->getReplayManager();
$context["nonce"] = $this->replayManager->getNonce();
return $this->renderTemplate("form.twig", $context);
}
protected function depositAction(){
$this->replayManager = $this->getReplayManager();
$nonce = $_POST["nonce"];
if (!$this->replayManager->checkNonce($nonce))
return $this->renderErrorTemplate("Nonce expired!");
deposit($_POST["amount"]);
return $this->renderTemplate('confirmation.twig');
}
protected function getSession() {
$session = $this->get('session');
$session->start();
return $session;
}
protected function getReplayManager() {
return new ReplayManager($this->getSession());
}
}
I'm not sure what ReplayManager does, but it looks to me as if it is not the right class to handle the 'nonce'. As the 'nonce' is ultimately stored in and retrieved from the session it should either be handled by the controller or abstracted out into its own class which is then passed in as a dependency. This will allow you to mock the nonce (sounds like a sitcom!) for testing.
In my experience problems in testing are actually problems with code design and should be considered a smell. In this case your problem stems from handling the nonce in the wrong place. A quick refactoring session should solve your testing problems.
It is possible to access the Symfony2 session from PHPUnit via the WebTestCase client. I think something like this should work:
public function testDepositSucceeds() {
$this->crawler = self::$client->request(
'GET',
'/deposit',
);
$session = $this->client->getContainer()->get('session');
$nonce = $session->get('nonce');
$this->crawler = self::$client->request(
'POST',
'/deposit',
array("amount" => 23, "nonce" => $nonce),
array(),
array()
);
$this->assertEquals(
"Deposit Confirmation",
$this->crawler->filter("title")->text());
}
EDIT:
Alternatively, if there is a problem getting the nonce value from the session, you could try replacing the two lines between the GET and POST requests above with:
$form = $crawler->selectButton('submit');
$nonce = $form->get('nonce')->getValue(); // replace 'nonce' with the actual name of the element

If form validation fails

I want to redirect people to a certain page if the form validations fails. However, I can’t quite figure out how.
If i redirect people at the REDIRECT HERE comment below, it also redirects when it loads up the form and causes a endless loop.
public function create() {
$this->form_validation->set_rules('email_adress', 'E-mail', 'required|valid_email|is_unique[users.email_adress]');
if ($this->form_validation->run() !== FALSE) {
// PASSED
}
else {
// REDIRECT HERE
}
$this->load->view('user_register_view');
}
How can I achieve this?
You would redirect like so:
redirect('insert_URI_here');
Or if you are passing it to another method in the same controller you could just do :
$this->method_name();
However, if you just drop that in your else statement, user_register_view will not load because $this->form_validation->run() will return false either on validation error or non submission of the form.
What you will have to do is add another check to look for validation errors. If validation has failed and there are no validation errors, then your form hasn't been submitted.
So you could do something like this:
public function create() {
$this->form_validation->set_rules('email_adress', 'E-mail', 'required|valid_email|is_unique[users.email_adress]');
if(($this->form_validation->run() == FALSE) && ($this->form_validation->error_string() == ''));
//form not submitted yet
$this->load->view('user_register_view');
else if ($this->form_validation->run()) {
// PASSED
} else {
//Validation errors
redirect('insert_URI_here');
}
}
You will have to play around with it. I think $this->form_validation->error_string() should return an empty string if not submitted, but it might be a null value or a false (sorry can't remember off the top of my head).

Symfony form values missing

I was writing a simple login form, everything works fine (validation etc.) but I can't get the values, there's my code:
public function executeIndex(sfWebRequest $request)
{
$this->getUser()->clearCredentials();
$this->getUser()->setAuthenticated(false);
$this->form = new LoginForm();
if ($request->isMethod('post') && $request->hasParameter('login')) {
$this->form->bind($request->getParameter('login'));
if ($this->form->isValid()) {
$this->getUser()->setAuthenticated(true);
$this->getUser()->addCredential('user');
$this->login = $this->form->getValue('login');
}
}
}
$this->login is NULL. Now I checked almost everything, the form is valid, isBound() is true, count() returns 3, I can see the values in my request:
parameterHolder:
action: index
login: { login: foo, password: foo, _csrf_token: 53ebddee1883d7e3d6575d6fb1707a15 }
module: login
BUT getValues() returns NULL, getValue('login') etc. returns NULL as well. How can it be?
And no, I don't want to use sfGuard-Plugins ;)
What about trying something like this
$form['value_name']->getValue()
Is it still NULL?
Also is it possible that you created a custom post validator?
Callback validation must return values back to caller:
return $values;