I've upgraded my app from Laravel 4.2 to Laravel 5.3. On an index page listing citations, I have an AJAX modal form to edit or view the login credentials for the citation. This was working fine in Laravel 4.2, but I cannot for the life of me get it to work in 5.3. After about 5 hours Googling and trying different things, I thought I would post it here so that someone way more experienced than me can point me in the right direction.
Here's the link on the index page:
<a style="cursor: pointer; " title= "Login Credentials" data-loopback="cit-pg-1" data-citationid="1079" class="getCitationdetails"><span class="glyphicon glyphicon-lock " title="Login Credentials"></span></a>
And here's the JavaScript:
<script type="text/javascript">
$(document).on('click','.getCitationdetails',function(){
var citationid = $(this).data('citationid');
var loopback = $(this).data('loopback');
$.ajax({
url : '/citation-password',
type:'post',
data : {citationid :citationid, loopback :loopback},
success:function(resp){
$('#AppendLoginDetails').html(resp);
$('#LoginCredentialsModal').modal('show');
$('.loadingDiv').hide();
},
error:function(){
alert('Error');
}
})
})
Here's my route:
Route::match(['get', 'post'], '/citation-password', 'CitationsController@citationpassword');
And here's the Controller method that generates the form on get and saves the data on post:
public function citationpassword()
{
if (Request::ajax()) {
$data = Request::all();
if (!$data['citationid']) {
return redirect('/citations')
->with('flash-danger', 'Missing citation id for Login credentials form!!');
}
// Save loopback variable if we have it in order to return user to the page where they came from; default return location is citations
$loopback = 'citations';
if (array_key_exists("loopback", $data)) {
$loopback = $data['loopback'];
}
$getcitationdetails = Citation::where('id', $data['citationid'])->select('id', 'site_id', 'username', 'password', 'login_email', 'login_notes')->first();
$getcitationdetails = json_decode(json_encode($getcitationdetails), true);
$getsitedetails = Site::where('id', $getcitationdetails['site_id'])->select(
'id',
'directory_username',
'directory_password',
'security_questions',
'email_account',
'email_account_password',
'email_account_name',
'google_user',
'google_pwd',
'name_of_google_account'
)->first();
$getsitedetails = json_decode(json_encode($getsitedetails), true);
$response ="";
$response .= '<form action="'.url('/citation-password').'" method="post">
<div class="modal-body">';
if (!empty($getsitedetails['directory_username'])) {
$response .= '<div class="form-group">
<label for="recipient-name" class="col-form-label">Default login credentials for this site:</label>
<p>Username: '.$getsitedetails['directory_username'].'
<br />Password: '.$getsitedetails['directory_password'].'
<br />Email account: '.$getsitedetails['email_account'].'
<br />Email password: '.$getsitedetails['email_account_password'].'
<br />Name on email account: '.$getsitedetails['email_account_name'].'
<br />Default security questions: '.$getsitedetails['security_questions'].'</p>
<p>Gmail account: '.$getsitedetails['google_user'].'
<br />Gmail password: '.$getsitedetails['google_pwd'].'
<br />Name on Gmail account: '.$getsitedetails['name_of_google_account'].'</p>
</div>';
}
$response .= '
<input type="hidden" name="_token" value="'.csrf_token() .'" />
<input type="hidden" name="citation_id" value="'.$data['citationid'].'" />
<input type="hidden" name="loopback" value="'.$loopback.'" />
<div class="form-group">
<label for="recipient-name" class="col-form-label">Username:</label>
<input type="text" class="form-control" name="username" value="'.$getcitationdetails['username'].'" autocomplete="off">
</div>
<div class="form-group">
<label for="message-text" class="col-form-label">Password:</label>
<input type="text" class="form-control" name="password" value="'.$getcitationdetails['password'].'" autocomplete="off">
</div>
<div class="form-group">
<label for="message-text" class="col-form-label">Login email used:</label>
<input type="text" class="form-control" name="login_email" value="'.$getcitationdetails['login_email'].'" autocomplete="off">
</div>
<div class="form-group">
<label for="message-text" class="col-form-label">Login notes:</label>
<textarea class="form-control" style="height:130px;" name="login_notes">'.$getcitationdetails['login_notes'].'</textarea>
</div>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-success" id="success">Save</button>
<button type="button" class="btn btn-danger" data-dismiss="modal" aria-hidden="true">Cancel</button>
</div>
</form>';
return $response;
} else {
// The popup modal has posted back here; process the data
$data = Request::all();
// Handle & translate loopback; returning user to the page where they came from
$loopback = 'citations';
if ($data['loopback']) {
$loopback = $data['loopback'];
// Translate pages it came from
$trackLoopback = new trackLoopback();
$loopback = $trackLoopback->translate($loopback);
}
$updatecitation = Citation::find($data['citation_id']);
$updatecitation->username = $data['username'];
$updatecitation->password = $data['password'];
$updatecitation->save();
return redirect($loopback)
->with('flash-success', 'Login credentials have been updated successfully!');
}
}
In an effort to isolate the error, I even simplified the form in the controller like this:
public function citationpassword()
{
if (Request::ajax()) {
return '<p>This is the modal form!</p>';
} else {
// The popup modal has posted back here; process the data
$data = Request::all();
// Handle & translate loopback; returning user to the page where they came from
$loopback = 'citations';
if ($data['loopback']) {
$loopback = $data['loopback'];
// Translate pages it came from
$trackLoopback = new trackLoopback();
$loopback = $trackLoopback->translate($loopback);
}
$updatecitation = Citation::find($data['citation_id']);
$updatecitation->username = $data['username'];
$updatecitation->password = $data['password'];
$updatecitation->save();
return redirect($loopback)
->with('flash-success', 'Login credentials have been updated successfully!');
}
}
and also simplified the route to this:
Route::get('/citation-password', 'CitationsController@citationpassword');
but all I get when I click the link is a popup notice, "Error."
I'm not experienced with AJAX. How do I get the form to display in Laravel 5.3?
And/or, how can I change the JavaScript function so that it shows the actual error instead of the "Error" notice? (I tried a number of methods I found on StackOverflow to display errors but all of them resulted in NO error notice; just a blank page. And, I've not been successful at getting my Firefox debugger to show the errors either.)
Thanks!
The correct way to debug the JavaScript is to post the errors this way:
Once you do so, you will see that the error has to do with missing CsrfToken for the form. [The actual error message is from the Laravel framework: Illuminate\Session\TokenMismatchException: in file /home/reviewsites/moxy53/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/VerifyCsrfToken.php on line 6] Since both the get and post verbs use the same route, Laravel is requiring the CsrfToken before the form with the Csrf field gets generated.
It is possible (but NOT recommended!) to exclude this route from CSRF protection by editing App\Http\Middleware\VerifyCsrfToken.php with the following exception:
However, a much better approach is to add the token. It is correct that since you are using a post method to send the data values to the controller, you cannot use the controller to generate the token field in the form. Hence, the solution is to take the html out of the controller and put it in the blade. These lines:
should not be in the $response generated by the controller, but should instead be in the modal div in the blade itself. THEN, you can add the CSRF field in the blade thus: