Log-in, Log-out, Signup and Password Reminder

Summary: Today I it's time to build a login form for the front-end part of our application. It will restrict access to paged such as "Videos" and "Account" until user is logged-in.

Front-end

Let's quickly create a basic index page layout which would contain a log-in form on the side.

templates/default/page/index.html
<div class="g-row">
	<div class="g-8">
        <p>Welcome to the DVD rental program. Lorem ipsum dolor sit amet</p>
        <?$Content?>
    </div>
    <div class="g-2">
        <h4>Customer Login</h3>
        <?$LoginForm?>

        <div style="float: left"><?$LoginButton?></div>
        <div style="float: right"><?$RegisterLink?><br/><?$ForgotLink?></div>
    </div>
</div>

Next, open page/index.php and instead of what there is right now, put this code in:

class page_index extends Page {
    function 
init(){
        
parent::init();

        
$form $this->add('Form',null,'LoginForm');
        
$form->setFormClass('vertical');
        
$form->addField('line','login');
        
$form->addfield('password','password');
        
$form->addSubmit('Login');

        if(
$form->isSubmitted()){

            
// Short-cuts
            
$auth=$this->api->auth;
            
$l=$form->get('login');
            
$p=$form->get('password');

            
// Manually encrypt password
            
$enc_p $auth->encryptPassword($p,$l);

            
// Manually verify login
            
if($auth->verifyCredentials($l,$enc_p)){

                
// Manually log-in
                
$auth->login($l);
                
$form->js()->univ()->redirect('video')->execute();
            }
            
$form->getElement('password')->displayFieldError('Incorrect login');
        }
    }
    function 
defaultTemplate(){
        return array(
'page/index');
    }
}

Let's go over the contens of that index page. First we create a simple form with 2 fields. setFormClass() makes field labels appear over the fields. When form is submitted, we are encrypting password with the standard Authentication class, verify credentials and if they match, manually login the user and redirect him to the "video" page. If login wasn't successful, display message about incorrect login instead. We've also switched to use our new template, which defines "LoginForm" tag.

Next we should configure system-wide Auth class for our frontend. Edit the lib/Frontend.php file to add this:

        $this->auth=$this->add('RentalAuth');
        
$this->auth->allow('demo','demo');

        
$menu $this->add('Menu',null,'Menu');
        if(
$this->auth){
            
$menu->addMenuItem('Home','video');
            
$menu->addMenuItem('account');
            
$menu->addMenuItem('logout');
        }else{
            
$menu->addMenuItem('Home','index');
        }

Remove previous declaration of the menu, as we have new one now. The last and important thing you need to do is to configure "auth" secret. You have already placed it into the admin/config.php. The secret should match and it's used in password hash generation. Changing the secret will also make it impossible to check hashes anymore, so be sure to keep it safe.

Edit config.php to add
$config['auth']['key']='secret';  // should match the one used by admin

Create the page/video.php containing list of available videos and make it protected from non-authenticated users.

class page_video extends Page {
    function 
init(){
        
parent::init();
        
$this->api->auth->check();

        
$this->add('MVCGrid')->setModel('Movie');
    }
}

You can now test your interface by trying to log-in with the user you have created on the back-end. Let's also add a simple user-management page

class page_account extends Page {
    function 
init(){
        
parent::init();

        
$this->api->auth->check();

        
$model $this->add('Model_Customer');
        
$model->getField('email')->system(true);
        
$this->add('FormAndSave')->setModel($model)->loadData($this->api->auth->get('id'));
    }
}

We don't want users to be able to change password as a regular fields, so we simply mark that field as a system before using the model on the FormAndSave. The FormAndSave widget is a simple extension of MVCForm which saves data into database once it's submitted. Nothing fancy.

Signup Form

The sign-up form is now really easy to create.

class page_signup extends Page {
    function 
init(){
        
parent::init();

        if(
$this->api->auth->isLoggedIn())$this->api->redirect('video');

        
$model=$this->add('Model_Customer');
        
$model->addField('password')->type('password')->mandatory(true);

        
$form $this->add('MVCForm');
        
$form->setModel($model);
        
$form->addField('password','confirm_password');
        
$form->addSubmit();
        

        
$form->onSubmit(function($form){
            if(
$form->get('password') != $form->get('confirm_password'))
                throw 
$form->exception('Passwords do not match')->setField('confirm_password');

            
$form->set('password',
                
$form->api->auth->encryptPassword($form->get('password'),$form->get('email')));

            
$form->update();

            
$form->js()->hide('slow')->univ()->successMessage('Registered successfully')->execute();
            
        });
    }
}

Finally you would need a "Sign-up" button on your login-form which would send user to the signup page:

        $form->addButton('Sign-up')->js('click')
            ->
univ()->location($this->api->getDestinationURL('signup'));