In this short part I’ll show you very simple approach for securing your admin area. And when I say very simple I really mean it. After this I will show you how with the use of JQuery we can easy add AJAX functionality to our application. I know about the integration with Dojo in the new releases of Zend Framework, but I really prefer JQuery to Dojo in my work, and I’m sure in the manual there will be nice examples about the usage of Dojo, so if you are interested to work with JQuery - you can see how simple you can use it with Zend Framework. If you won’t use JQuery - just skip the second half of this part - you won’t lose anything vital to the project.
So first we will add one more module to our application next to the default one - the admin module. Under the application/modules/ directory we create new subdirectory - admin. Then we add 2 subdirectories under application/modules/admin/ - controllers and views. We add empty IndexController.php file under application/modules/admin/controllers - we will add code here shortly. Then under the application/modules/admin/views/ we add 3 subfolders - filters, helpers and scripts. Under application/modules/admin/views/scripts we add one subfolder - index - this will hold the views for our Index Controller.
If you are lost with so many directory creations - here is what I have now
We will leave the meaningfull code in this admin module until the final part of this series, what we want now is to have placeholder text only. Before we have actual administration tools we want this area secure. So we add only one action - the default one. IndexController.php is really simple:
application/modules/admin/controllers/IndexController.php:
< ?php class Admin_IndexController extends Zend_Controller_Action { public function indexAction() { } }
And we will need just as simple view script:
application/modules/admin/views/scripts/index/index.phtml:
admin stuff here
Ok, now our brand new admin module is available at “http://localhost/admin” assuming you are developing on localhost with port 80 :). You can try it and you will see that you can open it even if you are not logged in. Lets change this. Recall that in our users DB table we have isAdmin field. I hope that you have some registered users by now, if you don’t have - please register one now and activate him (or ‘her’ if you prefer, but I will use ‘him’ because it is more natural). Now open your phpMyAdmin or whatever you use for DB administration and find the row in your users table for this user - this will be our administrator :). Change the isAdmin column in this row to be 1. If your user have username ‘admin’ you can use this:
UPDATE `users` SET `isAdmin`=1 WHERE `username`='admin';
Now we will write simple front controller plugin, which will make sure that only users with isAdmin flag set to 1 can access our admin module.
/application/Lib/Controller/Plugin/ACL.php
< ?php class Lib_Controller_Plugin_ACL extends Zend_Controller_Plugin_Abstract { /** * Called before an action is dispatched by Zend_Controller_Dispatcher. * * This callback allows for proxy or filter behavior. By altering the * request and resetting its dispatched flag (via * {@link Zend_Controller_Request_Abstract::setDispatched() setDispatched(false)}), * the current action may be skipped. * * In this version we have only one rule - for access to 'admin' module we require 'isAdmin' * flag of the user to be set to true (this is very simple, but for our current needs is enough) * * @param Zend_Controller_Request_Abstract $request * @return void */ public function preDispatch(Zend_Controller_Request_Abstract $request) { $module = $request->getModuleName(); if ($module == 'admin' && (!Zend_Registry::get('defSession')->currentUser || !Zend_Registry::get('defSession')->currentUser->isAdmin)) { // redirect to index if do not have access // (possible to redirect to some 'access denied' page if needed) $request->setModuleName('default')->setControllerName('index')->setActionName('index'); } } }
Thats all - on preDispatch (that is before each action is executed) we check the request object for the module name. If it is ‘admin’ and the current user is not admin - we replace the module name, controller name and action name with default/index/index. Better solution is to have special “access denied” page, but I’ll keep the things here as simple as possible. Now we just need to register this plugin with the front controller and we are done:
(in bootstrap.php):
$frontController->registerPlugin(new Lib_Controller_Plugin_ACL());
That’s all - you can open http://localhost/admin (again assuming that the project is on your localhost with port 80) and see the “admin stuff here” message only if you are logged in with user, who have isAdmin flag set to 1.
——————————****************************—————————
To part 2 of this part 6.
If you don’t want to use JQuery you can skip this. What is JQuery? A fast, concise, library that simplifies how to traverse HTML documents, handle events, perform animations, and add AJAX. At least they say it is this :). We want to use JQuery to add some form validation in the registration form.
So we download JQuery from JQuery site. Then put the jquery.js file in /public/js directory. Then create subdirectory there - /public/js/registration, here we will place our code for form validation. Actually here it is:
/public/js/register/form.js
$(document).ready(function() { $("#username").bind('keyup', usernameCheck); $("#email").bind('keyup', emailCheck); }); function usernameCheck() { var username = $("#username").val(); if (username.length < 3) { $("#username_help").html('This field should be at least 3 characters long'); } else if (username.length > 32) { $("#username_help").html('This field should be maximum 32 characters long'); } else { postObject = new Object; postObject.username = username; $.post('/user/checkUsernameAjax/', postObject, function(data){ if (data.valid) { $("#username_help").html(''); } else { $("#username_help").html('This username is already taken'); } }, "json" ); } } function emailCheck() { var email = $("#email").val(); { postObject = new Object; postObject.email = email; $.post('/user/checkEmailAjax/', postObject, function(data){ if (data.valid) { $("#email_help").html(''); } else { $("#email_help").html('This email is already registered'); } }, "json" ); } }
what we do here is to add 2 event listeners - to username and email fields. When the user types something there we will check if it is valid. The code, written with JQuery is so easy to read, that I feel stupid to explain lines of code such
if (username.length < 3) { $("#username_help").html('This field should be at least 3 characters long'); } else if (username.length > 32) { $("#username_help").html('This field should be maximum 32 characters long'); }
What is more interesting is the way we make AJAX (asynchronous) call to our application to check if the username (or the email) is not already taken. I use POST request here to prevent caching of the response, because if I use GET with something like “http://localhost/user/checkUsernameAjax/johny” to check if “johny” is available, then some cache machines, proxy servers or even your own browser will cache the response that johny is available, and after 3 minutes when some other johny actually register himself in the site, you will still get the message that “johny” is available if you check. We want to prevent this so use POST. Other way is to use GET with URL like “http://localhost/user/checkUsernameAjax/johny/someRandomNumberHere”. Anyway, as there are many ways to accomplish something we just pick the POST and go ahead. If you are new to JQuery, the sintax of $.post function may seems a little wired, but using anonymous functions is very common language structure in JQuery, so better get used to it. Of course we could have named function as callback here - which checks if data.valid is true or false, and for some more complex functions may be it is better to have named function. For our single IF-ELSE block function this approach is best.
What is missing from the picture is the “/user/checkUsernameAjax/” and “/user/checkEmailAjax/” actions in the user action controller. If you expect something very very complex now, think again
in /application/modules/default/controllers/UserController.php:
public function checkusernameajaxAction() { $this->_helper->layout->disableLayout(); $username = $this->getRequest()->getParam('username'); $usersTable = new Users(); $select = $usersTable->select(); $select->where('username = ?', $username); $rows = $usersTable->fetchAll($select); if ($rows->count()) $valid = false; else $valid = true; $this->_helper->viewRenderer->setNoRender(); $data = array('valid'=>$valid); $json = Zend_Json::encode($data); echo $json; } public function checkemailajaxAction() { $this->_helper->layout->disableLayout(); $email = $this->getRequest()->getParam('email'); $usersTable = new Users(); $select = $usersTable->select(); $select->where('email = ?', $email); $rows = $usersTable->fetchAll($select); if ($rows->count()) $valid = false; else $valid = true; $this->_helper->viewRenderer->setNoRender(); $data = array('valid'=>$valid); $json = Zend_Json::encode($data); echo $json; }
Here we want to return JSON encoded array with one boolean element - ‘valid’. So 1) we disable layout. 2) get the username or the email value which we have to check if is available 3) run the DB query 4) tell the view renderer action helper to not render any view 5) encode our answer and finally 6) print the answer to the client.
Now we will add ‘username_help’ and ‘email_help’ divs to the HTML markup of the registration form. Remember from our form.js that we write our help messages to this two divs. In the getRegistrationForm function in UserController.php we add this lines where we define the username and email fields:
$username->addDecorator(array('ajaxDiv' => 'HtmlTag'), array('tag'=>'div', 'placement'=>'append', 'id'=>'username_help', 'class'=>'errors'));
$email->addDecorator(array('ajaxDiv' => 'HtmlTag'), array('tag'=>'div', 'placement'=>'append', 'id'=>'email_help', 'class'=>'errors'));
The function is too big to paste here, and these are only 2 new lines, so I’ll better paste the context only:
$username->addValidator($validatorNotEmpty, true)->setRequired(true)->setLabel('Username') ->addFilter($filterTrim) ->addValidator($validatorAlnum) ->addValidator($validatorStringLength) ->addValidator($validatorUniqueUsername); $username->addDecorator(array('ajaxDiv' => 'HtmlTag'), array('tag'=>'div', 'placement'=>'append', 'id'=>'username_help', 'class'=>'errors')); $form->addElement($username);
$email->addValidator($validatorNotEmpty, true)->setRequired(true)->setLabel('Email Address') ->addFilter($filterTrim) ->addValidator($validatorEmail) ->addValidator($validatorUniqueEmail); $email->addDecorator(array('ajaxDiv' => 'HtmlTag'), array('tag'=>'div', 'placement'=>'append', 'id'=>'email_help', 'class'=>'errors')); $form->addElement($email);
And finally we need to tell the browser to load jquery.js and form.js files. We will use headScript view helper for this. At the end of the registerAction() function add these two lines:
$this->view->headScript()->appendFile('/js/jquery.js'); $this->view->headScript()->appendFile('/js/register/form.js');
and in application/layouts/main.phtml in the head section of the html add this:
< ?php echo $this->headScript(); ?>
Now it should be working.
To say this again - if you are reading this and do not know anything about JQuery, but you are excited how with just 10 lines of javascript code and some very simple php functions we added AJAX functionality to our application - read some more about JScript. What we have done here is really simple, after you understand it you will understand the great power, that you have in your hands.
There. I hope you enjoyed our time together today. You know it seems harder and harder to just sit back and enjoy the finer things in life. Well, until next time…ta ta!
(oops, here is the code after this part: part6.rar)














MPL | 20-Aug-08 at 12:42 am | Permalink
nice thx but the admin part doesnt work by me im not sure but i thing there missing something… im logged in with admin flag im open localhost/admin but nothing happens
viperx | 20-Aug-08 at 11:33 am | Permalink
well, nothing should happen
we have not written any admin functionality by now. All you have to see is “admin stuff here” message.
MPL | 20-Aug-08 at 3:01 pm | Permalink
yes but i cant see “admin stuff here” i see the normal page only
viperx | 20-Aug-08 at 5:19 pm | Permalink
It seems that you got redirected to default/index/index, so the check in ACL fails. Try some debugging. Change the IF clause there to be something like:
$module should be ‘admin’ when you access “http://localhost/admin” and the isAdmin flag of currentUser should be 1.
Bruno Friedmann | 20-Aug-08 at 6:00 pm | Permalink
Always a real pleasure to read and learn
I’ve just detected some inconstancy in the way the code is writted in views ( sometimes you use the short notation which should be banished from our code editors )
in index/index.phtml for example I have to replace
in else and endif …
isLoggedIn) : ?>
Hello username; ?>, this is your (not so yet) personalized home page
Hello anonymous user, you can register in the site by clicking here or login.
MPL | 20-Aug-08 at 10:01 pm | Permalink
i have got with the debbuging
string(5) “admin” NULL “
MPL | 20-Aug-08 at 10:05 pm | Permalink
sry wrong when im logged in go to /admin i have got nothing my username is MPL not admin is this the error maybe?
viperx | 20-Aug-08 at 10:54 pm | Permalink
@Bruno: yes, you are right
I try to use this full form
< ?php echo $xxx; ?> instead of < ?=$xxx?> but the old habit is bad thing :))
and I try to use the alternative “IF(): ENDIF;” construction, so the views are easier to read.. you are right that I still sometimes use the usual “if () {}” style - also old habit.
viperx | 20-Aug-08 at 11:04 pm | Permalink
@MPL:
the NULL means that “Zend_Registry::get(’defSession’)->currentUser” is not set, this should be set when you login (this is done in loginAction when you login)
some things that I suspect as potential problems: your session is not working correct or you have not logged in..
It is not important the name of the user, it is important the user to be with isAdmin flag set to 1.
I really can’t say what is the problem. Are you running the code from the RAR file?
Try doing some more debugging, the code in the RAR works fine if you have set up the DB corectly (the session depends on the DB)
MPL | 21-Aug-08 at 12:20 am | Permalink
thx now works ^^ have downloaded the code again
arnsvel | 28-Aug-08 at 5:27 am | Permalink
Wow!! this is great for a beginner like me.
I am using Postgre SQL could somebody help me configuring this code for posgre sql connection????
pleaaassseeee…..
yuki | 28-Aug-08 at 4:09 pm | Permalink
Thanks for the great tutorial.
Is there any particular reason why Lib_Controller_Plugin_ACL is located inside /application/Lib/… but not with the other custom classes /library/My/…
viperx | 28-Aug-08 at 5:04 pm | Permalink
Hi yuki,
Yes, the reason is that in /library/My/ I put code that I will use in the same form, without rewriting it in other projects. It is part of the library. In /application/Lib/ I put classes, that are specific for this application, but are not part of the Model part of the MVC. May be this is not the best solution, I have some doubts about it too, so I’m open for suggestions to improve this design
MPL | 16-Sep-08 at 11:05 pm | Permalink
next part ?^^
viperx | 18-Sep-08 at 3:10 pm | Permalink
Next part will be published shortly I hope. I’m doing 100 things at once.. I hope soon I can find a time to write the article to describe the next steps - lost password retrieval and profile editing.
mpl | 18-Sep-08 at 4:01 pm | Permalink
okay thx =) we are waiting^^
fx | 20-Sep-08 at 7:37 am | Permalink
hi, thanks for great tutor, but i’ve got strange error when i’m trying to get the URL user/register of this app:
Zend_Controller_Dispatcher_Exception: Invalid controller class (”UserController”) in C:\www\corksdev\library\Zend\Controller\Dispatcher\Standard.php on line 353
preceded by bunch of php codes (i reckon they are from UserController methods init & registerAction)
What could be wrong? I should mention that there is no such error when i’m loading in browser TestController (url: test/)
viperx | 23-Sep-08 at 11:43 am | Permalink
I can’t debug remotely your project, but you can try this: check if the UserController.php is in its place in controllers directory, then check if you don’t have some syntax errors in UserController.php..
you can download the code from the RAR archive and get the file from there
Ben | 02-Oct-08 at 4:52 pm | Permalink
This is a great series - just what I had been looking for to get me started on Zend. I really appreciate the step-by-step instructions.
Looking forward to the next part!
Ryan | 16-Oct-08 at 12:20 am | Permalink
When I try and access things other than the main page, I get some weird errors. Here is what I am working with and the errors I get:
application path: C:\wamp\www\ZendApp
(Contains Configuration, Public, Application, etc.)
Inside the bootstrapper I have changed the paths appropriately and the main page will work if I type in this URL: http://localhost/ZendApp/application/bootstrap.php
If I try to access the Public index, I get this error:
“Internal Server Error
The server encountered an internal error or misconfiguration and was unable to complete your request.
Please contact the server administrator, admin@localhost and inform them of the time the error occurred, and anything you might have done that may have caused the error.
More information about this error may be available in the server error log.”
Also, clicking on the links on the main page does not work. I get the 404 error if I click on a link (because it is going to localhost/user… and my files are located at localhost/ZendApp/)
Am I missing something in configuring apache or vhosts or is there something I can change to get it to work in a sub-folder? My .htaccess is set like in the tutorial, so I don’t know what the problem is. Thanks in advance!
viperx | 17-Oct-08 at 5:29 pm | Permalink
Hi Ryan.
I strongly reccomned you to make a virtual host for your zend framework project. If you really have to run it from subdirectory, please make sure all the links are relative and not absolute (so without / in front).
the main page of the project is not the bootstrap.php file but the index.php in the public directory