$Content?>
Summary: Today I will move administrator's interface into proper location, will set up password authentication for the front-end and will add ability for admin to change user passwords.
What we just did is the very basics of the User Interface - only the start. Since we got our client all excited let's listen to our further requirements or "user stories" how they properly call them in Agile Development.
Manager should be able to access and edit all this data through Administrator's interface. Users need to be able to use frontend to signup themselves. They also should be able to login with their email and password and see their own data. They should be able to see what they have rented currently and in the past.
Not mutch, but let's try to split it into the list of the further tasks we will focus on:
Right now we have just added the pages for how they are, but it would be probably a good idea to separate them into a separate interface only available to the managers. To do that, we will create a new Application Class, which will have it's own authentication rules and its own pages, but would share models.
Create a completely new directory called "admin" and inside there place the following files:
after you are done, move page/mgr.php into admin/page/mrg.php. You should be able now to add /admin/ at the end of your URL and be able to access your brand new administration interface. Location engine will properly look for models in the main lib directory, but pages must be defined locally to avoid unauthorized access.
With admin pages out of the way, we can now create authentication for our front-end users. Before that, however, we would need to perform database migration (alters). and the best way to do so would be to use "dbupdate" techniques.
Create file doc/dbupdates/rental-001-01.sql containing:
alter table customer add email varchar(255);
alter table customer add password varchar(255);
Next you would need to copy, or sym-link atk4/tools/update.sh into doc/ and execute it. If it complains about missing 'config.php', then copy config-defaults.php into config.php. You should see this in your terminal:
$ cd doc/
$ ln -s ../atk4/tools/update.sh .
$ ./update.sh
cat: ../../../config.php: No such file or directory
Error: Can't read config file
$ cd ..
$ cp config-default.php config.php
$ cd doc/
$ ./update.sh
* Applying updates on database 'project'
rental-001-01.sql... ok
* Done
$
Fine. Let's leave command-line for your admin. For now simply import file doc/dbupdates/rental-001-01.sql into your MySQL database.
/?>Next you need to update customer's models by defining new field "email". We specifically leave out password from the model. This way UI elements will not be able to access it.
// add into lib/Model/Customer.php, init(): $this->addField('email'); /?>We would like to create our own way for authenticating. There are two classes - BasicAuth and SQLAuth. While you could just use SQLAuth which already supports authentication against data-base, for the sake of excercise, let's use BaseAuth instead. It's very important that you learn how to create your own objects in Agile Toolkit using inheritance instead of only using existing ones. Create file lib/RentalAuth.php. (not admin/lib/RentalAuth.php!):
class RentalAuth extends BasicAuth { /** Initialize our auth. Set model and password encryption */ function init(){ parent::init(); $this->setModel('Model_Customer'); $this->getModel()->addField('password')->system(true); $this->usePasswordEncryption('sha256/salt'); } /** Do not show form, simply redirect to index page, if not authorized */ function check(){ if(!$this->isLoggedIn()){ $this->api->redirect('/'); } } /** Password validation routine, now using the model. */ function verifyCredentials($email,$password){ $model = $this->getModel()->tryLoadBy('email',$email); // Manually encrypt password $enc_p = $this->encryptPassword($password,$email); if(!$model->isInstanceLoaded())return false; if($enc_p == $model->get('password')){ $this->addInfo($model->get()); unset($this->info['password']); return true; }else return false; } } /?>When verification takes place, if it is successful we use addInfo() function which will accumulate more information about currently-logged user. Once this information is memorized, it will be accessible through $auth->get() at all times while user is logged-in.
And finally admin should be able to set passwords. Let's first create the User Interface for that. We are going to enhance standard CRUD a little. First, you should remember that CRUD object actually relies on MVCGrid and MVCForm to do all of it's work. After initializing CRUD you can interact with those components directly too. We will add new column to our CRUD. Open file admin/page/mgr.php and replace line containing add('CRUD')->setModel('Customer') with the following code:
$crud=$tabs->addTab('Customers')->add('CRUD'); $crud->setModel('Customer'); if($crud->grid){ $crud->grid->addColumn('prompt','set_password'); if($_GET['set_password']){ $auth = $this->add('RentalAuth'); $model = $auth->getModel()->loadData($_GET['set_password']); //$enc_p = $auth->encryptPassword($_GET['value'],$model->get('email')); //no longer needed as of version 4.2. Agile Toolkit 4.2 it will automatically add hook into user model to encrypt password when saved, but that only applies to the api->auth->mode $model->set('password',$_GET['value'])->update(); $this->js()->univ()->successMessage('Changed password for '.$model->get('email')) ->execute(); } } /?>Initialization code is executed during the initial render but also when the button is clicked. However during button-click the GET argument will be passed and output is expected to be JavaScript. The "prompt" column also will provide us with the "value" through the GET argument, which we will encrypt and save into database. Finally, we execute a chain to produce JavaScript action.
Password is common functionality. Isn't it a lot to do for simple functionality?
Agile Toolkit is designed not to limit developer in any way. Password changing routine might have been different, encryption and field names might have been different as well as the user interface or responses in password change might have been different. If you find that this technique works for you, you can create your own set of classes which you would re-use across all of your projects - "User" model, your default "Auth", "UserCRUD" which implements your user management.
Once you do that, you can re-use those piece of code in all of your applications or share with others in form of "addon"
/?>You should be able now to open /admin/ section in your browser, login with "admin" / "demo" and see the "Set Password" button in your user management section. Clicking on that button should ask you for a password, which should be then saved into a database encrypted. $Next?>