I am trying to implement the password reset logic. The user gets the link to reset the password in the email. The url looks like
http://example.com/reset/resetcode
I have the route defined for it:
Route::get('reset/{resetcode}', function(){
return View::make('users.reset_password');
});
The form is rendered in the view to submit the email, new password etc. For the post of the form I have route defined as:
Route::post('reset/{resetcode}', array( 'as' => 'reset', 'uses' => 'UserController#passwordReset'));
I grab the resetcode from post route inside the passwordReset controller below
public function passwordReset($resetcode)
{
$validation = Validator::make(Input::all(), UserModel::$rulesPasswordReset);
if ($validation->passes())
{
try
{
// Find the user using the user email address
$user = Sentry::findUserByLogin(Input::get('email'));
// Check if the reset password code is valid
if ($user->checkResetPasswordCode($resetcode))
{
// Attempt to reset the user password
if ($user->attemptResetPassword($resetcode, 'new_password'))
{
// Password reset passed
}
else
{
// Password reset failed
}
}
else
{
// The provided password reset code is Invalid
}
}
catch (Cartalyst\Sentry\Users\UserNotFoundException $e)
{
echo 'User was not found.';
}
}
else return Redirect::route('reset')->withInput()
->withErrors($validation)
->with('title', 'resetrequestfailure')
->with('message', 'Seems like you made some errors.');
}
The problem I am having is when I do Redirect::route after the validation fails. I am getting the resetcode from the route defined for post. When validation fails, the redirect route messes up and I cannot get the resetcode the second time. The supposed url of format
http://example.com/reset/8f1Z7wA4uVt7VemBpGSfaoI9mcjdEwtK8elCnQOb
becomes
http://bcnet.org/reset/%7Bcode%7D
It has to do with /{resetcode} part of the route and this is variable, so how can I get the correct resetcode even after the validation fails meaning that the url remains intact. Or how can I fix it to the appropriate Redirect::route after the validation failure.
You need to include the $resetcode on your return
else return Redirect::route('reset', $resetcode)->withInput()
->withErrors($validation)
I'm basically trying to create a multi-step form using the CActiveForm class in Yii. The idea is I want to use the built-in functionality to achieve this in the simplest way possible. The requirement I have is as follows:
A multi step ONE PAGE form (using DIVs that show/hide with jQuery)
AJAX validation on EACH step (validate step-specific attributes only)
The validation MUST work using the validateOnChange() and validateOnSubmit() methods
This is a half-working solution I have developed so far:
View:
<div class="form">
<?php $form = $this->beginWidget('CActiveForm', array(
'id'=>'listing-form',
'enableClientValidation'=>false,
'enableAjaxValidation'=>true,
'clientOptions'=>array(
'validateOnChange'=>true,
'validateOnSubmit'=>true,
'afterValidate'=>'js:validateListing',
),
)); ?>
<?php echo $form->errorSummary($model); ?>
<div class="step" id="step-1">
// model input fields
<?php echo CHtml::submitButton('Next Step', array('name'=>'step1')); ?>
</div>
<div class="step" id="step-2" style="display: none;">
// model input fields
<?php echo CHtml::submitButton('Next Step', array('name'=>'step2')); ?>
</div>
<div class="step" id="step-3" style="display: none;">
// model input fields
<?php echo CHtml::submitButton('Submit', array('name'=>'step3')); ?>
</div>
<?php $this->endWidget(); ?>
</div>
JavaScript:
function validateListing(form, data, hasError)
{
if(hasError)
{
// display JS flash message
}
else
{
if($('#step-1').css('display') != 'none')
{
$('#step-1').hide();
$('#step-2').show();
}
else if($('#step-2').css('display') != 'none')
{
$('#step-2').hide();
$('#step-3').show();
}
else if($('#step-3').css('display') != 'none')
{
return true; // trigger default form submit
}
}
}
Controller:
public function actionCreate()
{
$model = new Listing;
// step 1 ajax validation
if(isset($_POST['step1']))
{
$attributes = array('name', 'address1', 'etc');
$this->performAjaxValidation($model, $attributes);
}
// step 2 ajax validation
if(isset($_POST['step2']))
{
$attributes = array('category', 'type', 'etc');
$this->performAjaxValidation($model, $attributes);
}
// step 3 ajax validation
if(isset($_POST['step3']))
{
$attributes = array('details', 'source', 'etc');
$this->performAjaxValidation($model, $attributes);
}
// process regular POST
if(isset($_POST['Listing']))
{
$model->attributes = $_POST['Listing'];
if($model->validate()) // validate all attributes again to be sure
{
// perform save actions, redirect, etc
}
}
$this->render('create', array(
'model'=>$model,
));
}
protected function performAjaxValidation($model, $attributes=null)
{
if(isset($_POST['ajax']) && $_POST['ajax']==='listing-form')
{
echo CActiveForm::validate($model, $attributes);
Yii::app()->end();
}
}
To summarise. Basically what I have is a form with 3 submit buttons (one for each step). In my controller I check which submit button was pressed and I run AJAX validation for the attributes specific to that step.
I use a custom afterValidate() function to show/hide the steps upon submit. On step 3, the default form submit is triggered, which posts all the form attributes to the controller.
This works well, except it won't work with validateOnChange() (since the submit button doesn't get posted). Also I was wondering whether this is actually the best way to do this, or if anyone knows of a better way?
Thanks.
I'd suggesting using scenarios to turn on and off the appropriate rules. Adjust the model scenario based on what is sent to your controller.
Note: this may also be a really good place to use a CFormModel instead of a CActiveRecord, depending on what is in your form.
Edit: can you add a hidden field to each div section that contains the info about what step you are on? Seems like that should work instead of your submit buttons.
OPTION 1
When you do not receive a button, why not validate the entire form, why do you need to validate only specific attributes? Yii will validate the entire model, send back all the errors but only that particular error will be shown by the active form because that is how it works already.
OPTION 2
You can have 3 forms (not 1 like you have now), 1 on each step. Also create 3 scenarios 1 for each step.
Each form has a hidden field that gets posted with the form, it can actually be the scenario name just validate it when it comes in. Validate the model using this hidden field to set the scenario you are on.
You can cache parts on the model when the form is submitted successfully and at the end you have the complete model.
you can always have custom validation and it won't break your normal form validation
in your model
private $step1 = false;
private $step2 = false;
private $all_ok = false;
protected function beforeValidate()
{
if(!empty($this->attr1) && $this->attr2) // if the fields you are looking for are filled, let it go to next
{
$this->step1 = true;
}
if($this->step1)
{
... some more validation
$this->step2 = true;
}
if($this->step2)
{
... if all your logic meets
$this->all_ok = true;
}
// if all fields that your looking for are filled, let parent validate them all
// if they don't go with their original rules, parent will notify
if($this->all_ok)
return parent::beforeValidate();
$this->addError($this->tableSchema->primaryKey, 'please fillout the form correctly');
return false;
}
I think better create specific class for each step of validation and use scenarios with rules. Below is small example.
//protected/extensions/validators
class StepOneMyModelValidator extends CValidator
{
/**
* #inheritdoc
*/
protected function validateAttribute($object, $attribute)
{
/* #var $object YourModel */
// validation step 1 here.
if (exist_problems) {
$object->addError($attribute, 'step1 is failed');
}
...
Create other classes(steps) for validation...
// in your model
public function rules()
{
return array(
array('attr', 'ext.validators.StepOneMyModelValidator', 'on' => 'step1'),
...
How to use in controller:
$model = new Listing();
$steps = array('step1', 'step2', /* etc... */);
foreach($_POST as $key => $val) {
if (in_array($key, $steps)) {
$model->setScenario($key);
break;
}
}
$model->validate();
echo '<pre>';
print_r($model->getErrors());
echo '</pre>';
die();
Or we can validate all steps in one validator.
I made a "My bookmarks" tab on the user profile page using Views. The tab shows nodes the user has flagged.
However - "My bookmarks" should only be visible on the user's own profile page and at the moment the "My bookmarks" tab is visible on every profile a user visits. How do I check whether the current user matches the profile being viewed? I tried that from the View interface, but the access permissions don't have any options that work.
EDIT:
I think it is this code, but I still need some guidelines as to how to implement that:
<?php
global $user;
if (arg(0) == 'user' && $user->uid == arg(1)){
return TRUE;
}
else {
return FALSE;
}
?>
I also found this module, I think it helps a lot Views Access Callback
I managed to solve this using the code and module from above.
The custom module contains this code
<?php
function MYMODULE_views_access_callbacks() {
return array(
'MYCALLBACK_user_has_access' => t('User can only see tab on his own profile'));
}
function MYCALLBACK_user_has_access() {
global $user;
if (arg(0) == 'user' && $user->uid == arg(1)){
return TRUE;
}
else {
return FALSE;
}
}
?>
The Views Access Callback module adds your callback to the Views interface and from there, you can use it for your own view.
I created a simple login form that posts data to _self and then I tried to check the credentials in order to log the user in.
The authenticating script looks like that:
if ($_POST['username'] && $_POST['password'] )
{
$db =& JFactory::getDBO();
$query = 'SELECT `id`'
. ' FROM #__users'
. ' WHERE username='.$_POST['username'];
$db->setQuery( $query );
$result = $db->loadResult();
if (!$result) {
echo 'User does not exist';
}
if($result && (USERNAME & PASS MATCH))
{
<< LOGIN AND REDIRECT TO CERTAIN PAGE >>
}
else
{
echo 'Invalid username or password';
}
}
In my simple php scripts, I used to set a SESSION variable to true when username and password match and thats how I considered a user to be logged in.
Here, what actions should I do? Is there a field in the database that is set when user is logged in? In other words, I want to log the user in and then redirect to another page.
Any suggestions?
Once the user is logged in, you could do something like:
if($result && (USERNAME & PASS MATCH))
{
$_SESSION['logged_in_user'] = $user;
header("location: /loggedin.php");
}
Then at the top of loggedin.php set an authentication check:
session_start(); // If
if (!isset($_SESSION['logged_in_user'])) {
// User is not logged in, so send user away.
header("Location:/login");
die();
}
// User is logged in; private code goes here.
This is a great tutorial for user login systems: http://en.wikibooks.org/wiki/PHP_Programming/User_login_systems
It goes into advanced user authentication and shows more examples than I have here.
I'm just working with the Zend_Form in Zend Framework and came across something pretty weird.
I have the following inside my loginAction
$form = new Application_Model_FormLogin();
if ($this->getRequest()->isPost()) {
$email = $form->getValue('email');
$pswd = $form->getValue('pswd');
echo "<p>Your e-mail is {$email}, and password is {$pswd}</p>";
}
Which when submitted only outputs
Your e-mail is, and password is
So I checked to see what's going on with print_r ,
print_r($form->getValues());
print_r($_POST);
Which displayed the following,
Array ( [email] => [pswd] => ) Array ( [email] => asd [pswd] => asd [submit] => Login )
So the forms values array has both values as null and the global post array had the correct values. Now I can't work out the problem?
Now I did manage to fix the problem, but I need help understanding why this works? All I did was change the loginAction to this.
$form = new Application_Model_FormLogin();
if ($this->getRequest()->isPost()) {
//Added this in
if ($form->isValid($this->_request->getPost())) {
$email = $form->getValue('email');
$pswd = $form->getValue('pswd');
echo "<p>Your e-mail is {$email}, and password is {$pswd}</p>";
}
}
I don't get how this made it work? Considering there is no validation on the fields?
Any thoughts? All I can think is maybe I have something setup weird in my server configuration?
Thanks
You didnt load the Values in your form object.
Normaly you check if the form is valid and for this load it with the post data, in the next step you can use getValue() to get the (filtered) value from the form.
if($this->getRequest()->isPost()) {
$form = new My_Form();
if($form->isValid($this->getRequest()->getPost())){
echo $form->getValue('fieldname');
}
}
isValid() is what actually populates the fields in your form object, until you do that the values do not exist in your form object yet.
modifying your original code would be as simple as this
if ($this->getRequest()->isPost()) {
//your $form object has none of your POSTed values
$form->isValid($this->getRequest()->getPost())
//now your form object has the POSTed values and you can access them
$email = $form->getValue('email');
$pswd = $form->getValue('pswd');
echo "<p>Your e-mail is {$email}, and password is {$pswd}</p>";
}
This skims over it extremely lightly http://framework.zend.com/manual/1.11/en/zend.form.quickstart.html#zend.form.quickstart.validate
Consider this example also and it might make more sense. Here you just grab the values from the POST.
if ($this->getRequest()->isPost()) {
$email = $this->getRequest()->getPost('email');
$password = $this->getRequest()->getPost('password');
echo "<p> Your email is $email and your password is $password </p>";
}