Yii: Adding Users and User Management to Your Site

Exclusive offer: get 50% off this eBook here
Yii Rapid Application Development Hotshot

Yii Rapid Application Development Hotshot — Save 50%

Become a RAD hotshot with Yii, the world's most popular PHP framework with this book and ebook.

$29.99    $15.00
by James R. Hamilton III Lauren J. O'Meara | February 2013 | Open Source PHP Web Development

As web developers, we are always looking for new and better tools to help us develop quality websites. Yii caught our eye as a great framework. It is known for performance. In addition to its speed, Yii provides great tools and features to help you get your job done quickly.

In this article by Lauren J. O'Meara and James R. Hamilton III, authors of Yii Rapid Application Development Hotshot, we will add a user table to the application database, and then generate the Yii scaffolding and customize it. We will extend the user management interface to utilize our user table fields.

 

(For more resources related to this topic, see here.)

Mission Checklist

This project assumes that you have a web development environment prepared. The files for this project include a Yii project directory with a database schema. To prepare for the project, carry out the following steps replacing the username lomeara with your own username:

  1. Copy the project files into your working directory.

    cp –r ~/Downloads/project_files/Chapter\ 3/project_files ~/projects/ch3

  2. Make the directories that Yii uses web writeable.

    cd ~/projects/ch3/ sudo chown -R lomeara:www-data protected/runtime assets protected/models protected/controllers protected/views

  3. If you have a link for a previous project, remove it from the webroot directory.

    rm /opt/lampp/htdocs/cddb

  4. Create a link in the webroot directory to the copied directory.

    cd /opt/lampp/htdocs sudo ln -s ~/projects/ch3 cbdb

  5. Import the project into NetBeans (remember to set the project URL to http://localhost/cbdb) and configure for Yii development with PHPUnit.

  6. Create a database named cbdb and load the database schema (~/projects/ch3/ protected/data/schema.sql) into it.

  7. If you are not using the XAMPP stack or if your access to MySQL is password protected, you should review and update the Yii configuration file (in NetBeans it is ch3/Source Files/protected/config/main.php).

Adding a User Object with CRUD

As a foundation for our user management system, we will add a User table to the database and then use Gii to build a quick functional interface.

Engage Thrusters

  1. Let's set the first building block by adding a User table containing the following information:

    • A username

    • Password hash

    • Reference to a person entry for first name and last name

    In NetBeans, open a SQL Command window for the cbdb database and run the following command:

    CREATE TABLE 'user' ( 'id' int(10) unsigned NOT NULL AUTO_INCREMENT, 'username' varchar(20) NOT NULL, 'pwd_hash' char(34) NOT NULL, 'person_id' int(10) unsigned NOT NULL, PRIMARY KEY ('id'), UNIQUE KEY 'username' ('username'), CONSTRAINT 'userperson_ibfk_2' FOREIGN KEY ('person_id') REFERENCES 'person' ('id') ON DELETE CASCADE ) ENGINE=InnoDB;

  2. Open a web browser to the Gii URL http://localhost/cbdb/index.php/gii(the password configured in the sample code is yiibook) and use Gii to generate a model from the user table.

  3. Then, use Gii to generate CRUD from the user model.

  4. Back in NetBeans, add a link to the user index in your site's logged in menu (ch3 | Source Files | protected | views | layouts | main.php). It should look like this:

    } else { $this->widget('zii.widgets.CMenu',array( 'activeCssClass' => 'active', 'activateParents' => true, 'items'=>array( array('label'=>'Home', 'url'=>array('/site/index')), array('label'=>'Comic Books', 'url'=>array('/book'), 'items' => array( array('label'=>'Publishers', 'url'=>array('/publisher')), ) ), array('label'=>'Users', 'url'=>array('/user/index')), array('label'=>'Logout ('.Yii::app()->user- >name.')', 'url'=>array('/site/logout')) ), )); } ?>

  5. Right-click on the project name, run the site, and log in with the default username and password (admin/admin). You will see a menu that includes a link named Users.

  6. If you click on the Users link in the menu and then click on Create User, you will see a pretty awful-looking user-creation screen. We are going to fix that. First, we will update the user form to include fields for first name, last name, password, and repeat password. Edit ch3 | Source Files | protected | views | user | _form.php and add those fields.

  7. Start by changing all instances of $model to $user. Then, add a call to errorSummary on the person data under the errorSummary call on user.

    <?php echo $form->errorSummary($user); ?> <?php echo $form->errorSummary($person); ?>

  8. Add rows for first name and last name at the beginning of the form.

    <div class="row"> <?php echo $form->labelEx($person,'fname'); ?> <?php echo $form->textField($person,'fname',array ('size'=>20,'maxlength'=>20)); ?> <?php echo $form->error($person,'fname'); ?> </div> <div class="row"> <?php echo $form->labelEx($person,'lname'); ?> <?php echo $form->textField($person,'lname',array ('size'=>20,'maxlength'=>20)); ?> <?php echo $form->error($person,'lname'); ?> </div>

  9. Replace the pwd_hash row with the following two rows:

    <div class="row"> <?php echo $form->labelEx($user,'password'); ?> <?php echo $form->passwordField($user,'password',array ('size'=>20,'maxlength'=>64)); ?> <?php echo $form->error($user,'password'); ?> </div> <div class="row"> <?php echo $form->labelEx($user,'password_repeat'); ?> <?php echo $form->passwordField($user,'password_repeat',array ('size'=>20,'maxlength'=>64)); ?> <?php echo $form->error($user,'password_repeat'); ?> </div>

  10. Finally, remove the row for person_id.

  11. These changes are going to completely break the User create/update form for the time being.

    We want to capture the password data and ultimately make a hash out of it to store securely in the database. To collect the form inputs, we will add password fields to the User model that do not correspond to values in the database. Edit the User model ch3 | Source Files | protected | models | User.php and add two public variables to the class:

    class User extends CActiveRecord { public $password; public $password_repeat;

  12. In the same User model file, modify the attribute labels function to include labels for the new password fields.

    public function attributeLabels() { return array( 'id' => 'ID', 'username' => 'Username', 'password' => 'Password', 'password_repeat' => 'Password Repeat' ); }

  13. In the same User model file, update the rules function with the following rules:

    • Require username

    • Limit length of username and password

    • Compare password with password repeat

    • Accept only safe values for username and password

    We will come back to this and improve it, but for now, it should look like the following:

    public function rules() { // NOTE: you should only define rules for those attributes //that will receive user inputs. return array( array('username', 'required'), array('username', 'length', 'max'=>20), array('password', 'length', 'max'=>32), array('password', 'compare'), array('password_repeat', 'safe'), ); }

  14. In order to store the user's first and last name, we must change the Create action in the User controller ch3 | Source Files | protected | controllers | UserController. php to create a Person object in addition to a User object.

    Change the variable name $model to $user, and add an instance of the Person model.

    public function actionCreate() { $user=new User; $person=new Person; // Uncomment the following line if AJAX validation is //needed // $this->performAjaxValidation($user); if(isset($_POST['User'])) { $user->attributes=$_POST['User']; if($user->save()) $this->redirect(array('view','id'=>$user->id)); } $this->render('create',array( 'user'=>$user, 'person'=>$person, )); }

  15. Don't reload the create user page yet. First, update the last line of the User Create view ch3 | Source Files | protected | views | user | create.php to send a User object and a Person object.

    <?php echo $this->renderPartial('_form', array('user'=>$user, 'person' =>$person)); ?>

  16. Make a change to the attributeLabels function in the Person model (ch3 | Source Files | protected | models | Person.php) to display clearer labels for first name and last name.

    public function attributeLabels() { return array( 'id' => 'ID', 'fname' => 'First Name', 'lname' => 'Last Name', ); }

  17. The resulting user form should look like this:

  18. Looks pretty good, but if you try to submit the form, you will receive an error. To fix this, we will change the User Create action in the User controller ch3 | Source Files | protected | controllers | UserController.php to check and save both User and Person data.

    if(isset($_POST['User'], $_POST['Person'])) { $person->attributes=$_POST['Person']; if($person->save()) { $user->attributes=$_POST['User']; $user->person_id = $person->id; if($user->save()) $this->redirect(array('view','id'=>$user->id)); } }

  19. Great! Now you can create users, but if you try to edit a user entry, you see another error. This fix will require a couple of more changes.

    First, in the user controller ch3 | Source Files | protected | controllers | UserController.php, change the loadModel function to load the user model with its related person information:

    $model=User::model() ->with('person') ->findByPk((int)$id);

  20. Next, in the same file, change the actionUpdate function. Add a call to save the person data, if the user save succeeds:

    if($model->save()) { $model->person->attributes=$_POST['Person']; $model->person->save(); $this->redirect(array('view','id'=>$model->id)); }

  21. Then, in the user update view ch3 | Source Files | protected | views | user | update.php, add the person information to the form render.

    <?php echo $this->renderPartial('_form', array('user'=>$model, 'person' => $model->person)); ?>

  22. One more piece of user management housekeeping; try deleting a user. Look in the database for the user and the person info. Oops. Didn't clean up after itself, did it? Update the User controller ch3 | Source Files | protected | controllers | UserController.php once again. Change the call to delete in the User delete action:

    $this->loadModel($id)->person->delete();

Objective Complete - Mini Debriefing

We have added a new object, User, to our site, and associated it with the Person object to capture the user's first and last name. Gii helped us get the basic structure of our user management function in place, and then we altered the model, view, and controller to bring the pieces together.

Yii Rapid Application Development Hotshot Become a RAD hotshot with Yii, the world's most popular PHP framework with this book and ebook.
Published: December 2012
eBook Price: $29.99
Book Price: $49.99
See more
Select your format and quantity:

Making a User Management Interface

The default Yii object index provides a nice summary listing of the user entries, but for many applications, it is more efficient to have a quick search capability. For this, Yii provides an additional "admin" view. We are going to completely replace the default listing with the admin view and update the scaffold view with a better integration of User with Person information for searching and sorting.

Engage Thrusters

  1. Delete the file ch3 | Source Files | protected | views | user | index.php.

  2. Rename the file ch3 | Source Files | protected | views | user | admin.php to index.php.

  3. In the files create.php, update.php, and view.php in ch3 | Source Files | protected | views | user, remove the following line from the menu array:

    array('label'=>'Manage User', 'url'=>array('admin')),

  4. In the User controller ch3 | Source Files | protected | controllers | UserController. php, delete the function named actionIndex.

  5. Also in the User controller, remove the admin accessRule for the admin action. The admin accessRule should look like the following:

    array('allow', // allow admin user to perform 'delete' actions 'actions'=>array('delete'), 'users'=>array('admin'), ),

    Also, change the redirect in the delete action to send to the index.

    $this->redirect(isset($_POST['returnUrl']) ? $_POST['returnUrl'] : array('index'));

  6. In the same file, rename the function actionAdmin to actionIndex, and change the call to render in the newly renamed actionIndex function to render to index instead of admin. Now, if you click on the Users link in the menu, you will see a user management grid, instead of a list of user entries.

    However, the information in the grid could be more useful.

  7. Edit the new user index ch3 | Source Files | protected | views | user | index.php. Remove the unnecessary column values id, pwd_hash, and person_id from the view. Add the columns we do want to see, namely first name and last name. These fields come from a related object, so their entries will look a little different. The file should look as follows:

    'columns'=>array( 'username', array( 'name' => 'person_fname', 'header' => 'First Name', 'value' => '$data->person->fname', ), array( 'name' => 'person_lname', 'header' => 'Last Name', 'value' => '$data->person->lname', ), array( 'class'=>'CButtonColumn', ), ),

    The entries for first name and last name include:

    • A name value, which is the name of the search variable

    • A header value, which is the column label

    • A data value, which is the data field that will populate the column output

  8. Edit the user model ch3 | Source Files | protected | models | User.php, and add public variables to catch the search fields person_fname and person_lname:

    class User extends CActiveRecord { public $password; public $password_repeat; public $person_fname; public $person_lname;

  9. In the same file, add a search field entry to the rules function with username, person_fname, and person_lname:

    public function rules() { // NOTE: you should only define rules for //those attributes that will receive user inputs. return array( array('username', 'required'), array('username', 'length', 'max'=>20), array('password', 'length', 'max'=>32), array('password', 'compare'), array('password_repeat', 'safe'), array('username, person_fname, person_lname', 'safe', 'on'=>'search'), ); }

  10. The search function will require the most changes. We will need to remove the unused fields (id, pwd_hash, and person_id), update the username field to indicate that it is from the base model, add the person relationship to the criteria, and add comparisons of the related fields (first and last name).

    public function search() { $criteria=new CDbCriteria; $criteria->compare('t.username',$this- >username,true); $criteria->compare('person.fname',$this- >person_fname,true); $criteria->compare('person.lname',$this- >person_lname,true); $criteria->with = array('person'); return new CActiveDataProvider($this, array( 'criteria'=>$criteria, )); }

    Now the grid will display all of the fields perfectly.

    However, you can only sort on the username column.

  11. In the User model search function, add a sort object with first name and last name fields and include it in the data provider to activate sort on the first name and last name columns.

    $sort = new CSort; $sort->attributes = array( 'person_fname' => array( 'asc' => 'person.fname', 'desc' => 'person.fname DESC', ), 'person_lname' => array( 'asc' => 'person.lname', 'desc' => 'person.lname DESC', ), '*', ); return new CActiveDataProvider($this, array( 'criteria'=>$criteria, 'sort' => $sort, ));

  12. Oh! One more thing. Have you clicked on the Advanced Search link yet?

    That doesn't look great.

    We can clean up the advanced search form ch3 | Source Files | protected | views | users | _search.php. Remove ID and password hash fields, and add first name and last name fields.

    <div class="row"> <?php echo $form->label($model,'username'); ?> <?php echo $form->textField($model,'username',array ('size'=>20,'maxlength'=>20)); ?> </div> <div class="row"> <?php echo $form->label($model,'First Name'); ?> <?php echo $form->textField($model,'person_fname',array ('size'=>10,'maxlength'=>10)); ?> </div> <div class="row"> <?php echo $form->label($model,'Last Name'); ?> <?php echo $form->textField($model,'person_lname',array ('size'=>10,'maxlength'=>10)); ?> </div>

    Now it looks good!

  13. The last view we will change is named View. Edit ch3 | Source Files | protected | views | users | view.php. Delete id, pwd_hash, and person_id from the attributes array. Add person_fname and person_lname to the list.

    array( 'name' => 'person_fname', 'header' => 'First Name', 'value' => $model->person->fname, ), array( 'name' => 'person_lname', 'header' => 'Last Name', 'value' => $model->person->lname, ),

Objective Complete - Mini Debriefing

We have removed the original user index and replaced it with the admin page that Yii provides, but modified so that the information is relevant, searchable, and sortable.

Classified Intel

We could make the index even more compact by moving the Create User link into the page, perhaps incorporating it into the grid, and removing the side menu, because the only other link is back to the index, and changing the layout to one column, instead of two.

Storing Passwords

In this task, we will add a hashing function and store the hashed password values in the database.

Engage Thrusters

  1. We have a nice user management interface, but if you open a SQL command window and query the user table, you will see that the password field for each user is empty.

    select * from user;

  2. We need to store the password, and in order to do that, we need to make a function to hash passwords. We will implement this function in the User model and do it in a rather simplistic way, using the crypt library that comes with PHP and providing no salt value, so that it is randomly generated by the library. You can replace this function with your own preferred method of hashing.

    public function hash($value) { return crypt($value); }

  3. Next, we need to call the encryption function whenever we store a password – on create and on update – so we will overload the beforeSave function to do the hashing. Add the following function to the User model:

    protected function beforeSave() { if (parent::beforeSave()) $this->pwd_hash = $this->hash($this->password); return true; } return false; }

    Now, if you add or update a user and look at the user table, you will see a hash value in your user entry.

  4. In preparation for logging in, let's go ahead and add a function to check a password value against the hashed value.

    public function check($value) { $new_hash = crypt($value, $this->pwd_hash); if ($new_hash == $this->pwd_hash) { return true; } return false; }

Objective Complete - Mini Debriefing

We have added a hashing function to our User model to perform one way hashing on password values, then applied the hashing function to password values after they have been validated. We prepared for the next step by adding a hash check function to the User model as well. At this point, the hashing will not be applied to the login, but in the next task, we will activate it.

Yii Rapid Application Development Hotshot Become a RAD hotshot with Yii, the world's most popular PHP framework with this book and ebook.
Published: December 2012
eBook Price: $29.99
Book Price: $49.99
See more
Select your format and quantity:

Activating Database User Login

In this task, we will convert the login action from the default authentication system provided by Yii to the authentication we have prepared in the previous tasks.

Prepare for Lift Off

We are about to cut over to a new authentication system. Before we do, be sure to create a user for yourself with a password that you know! If you haven't already, log in as admin/admin, go to the Users screen, create a user named admin with a password test. You can give this user whatever first and last name you like. We are about to use it to log in.

Engage Thrusters

  1. Edit the UserIdentity file ch3 | Source Files | protected | components | UserIdentity.php and replace the contents of the authenticate function with the following:

    $user = User::model() ->findByAttributes(array( 'username' => $this->username )); if($user===null) $this->errorCode=self::ERROR_USERNAME_INVALID; else if ($user->check($this->password)) { $this->errorCode=self::ERROR_NONE; } else $this->errorCode=self::ERROR_PASSWORD_INVALID; return !$this->errorCode;

    Now, the authenticate function will look in the database for the provided username. If that user is found, it will check the provided password against the user's password hash.

  2. Give it a try. Log out (if you are logged in), and try logging back in as admin/admin. Now log in with the admin user we created earlier ( ).

  3. Before we forget, edit the login view ch3 | Source Files | protected | views | site | login.php and remove the demo and admin user hint.

Objective Complete - Mini Debriefing

Now, instead of having to edit the UserIdentity file, and hardcode in another user/ password combination, you can use the web interface to create as many users as you like. If there is a user who no longer needs access to the system, you can delete the user and his/ her credentials will no longer work. This approach will be much easier to maintain.

Enforcing Secure Passwords

Looking again at user creation, we can see another problem. You can create a user with no password. That is not so bad, because the login form requires a password. If your user has no password, he will not be able to login, but what about the quality of the passwords? If you try to enter a one-character password, no problem, you can do it. This might be ok if you are the only person creating users and entering passwords. You can be careful to give your users passwords that are difficult to guess. You can devise and enforce your own password strength requirements, but typically, sooner or later, you are going to let your users set their own passwords. When this happens, you will want to enforce some checking to make sure the passwords your users set are difficult to guess. Otherwise, your users and your site are vulnerable to password cracking.

You can use this basic pattern for applying a password strength scheme and implement your own password strength requirements that are appropriate to your site. We will go with a basic requirement of a minimum length of eight characters, including at least one capital character, at least one number, and at least one non-alphanumeric character.

This pattern is also useful for implementing any custom validation rule.

Engage Thrusters

  1. Open the user model file for edit (ch3 | Source Files | protected | models | User.php).

  2. Add a function named passwordStrengthOk as follows:

    public function passwordStrengthOk($attribute, $params) { // default to true $valid = true; // at least one number $valid = $valid && preg_match ('/.*[\d].*/', $this->$attribute); // at least one non-word character $valid = $valid && preg_match ('/.*[\W].*/', $this->$attribute); // at least one capital letter $valid = $valid && preg_match ('/.*[A-Z].*/', $this->$attribute); if (!$valid) $this->addError($attribute, "Does not meet password requirements."); return $valid; }

  3. Add two new rules to the validation array:

    • Require a unique username.

    • Add the new rule we created, passwordStrengthOk and change one old rule. Add a check for minimum password length of 8 to the password length requirements.

      return array( array('username', 'required'), array('username', 'unique'), array('password, password_repeat', 'required'), array('username', 'length', 'min' => 3, 'max'=>20), array('password', 'length', 'min' => 8, 'max'=>32), array('password', 'compare', 'compareAttribute' => 'password_repeat'), array('password', 'passwordStrengthOk'), array('username, password, password_repeat', 'safe'), array('username, person_fname, person_lname', 'safe', 'on'=>'search'), );

  4. But what if we want to update something about the user, such as change the username, and not enter a new password? To do this, we will use a scenario. First, update the rules that apply to passwords and add the scenario parameter, so that the rules are only applied when the scenario is in play.

    return array( array('username', 'required'), array('username', 'unique'), array('password, password_repeat', 'required', 'on' => 'passwordset'), array('username', 'length', 'min' => 3, 'max'=>20), array('password', 'length', 'min' => 8, 'max'=>32, 'on' => 'passwordset'), array('password', 'compare', 'compareAttribute' => 'password_repeat'), array('password', 'passwordStrengthOk', 'on' => 'passwordset'), array('username, password, password_repeat', 'safe'), array('username, person_fname, person_lname', 'safe', 'on'=>'search'), );

    i. Then, in the User controller, activate the passwordset scenario whenever we want the passwordset rules to apply. In the Create function, we always want the scenario to apply, so pass it to the model constructor at the start.

    $user=new User('passwordset');

    ii. In the Update function, we only want to apply the scenario when a password field has been entered, so set the scenario on the model conditionally. After the attributes are gathered from the form, if the password or password_repeat value has been set, apply the scenario.

    $model->attributes=$_POST['User']; if ($model->password || $model->password_repeat) $model->scenario = 'passwordset';

  5. Let's make sure we did all of that correctly by making and running a functional test.

  6. First, we will augment our testing setup by downloading the Selenium standalone server from http://seleniumhq.org/.

  7. Then, update the phpunit config to define the browsers that you will test against. In our example, we will test against Firefox (of course, you must have Firefox installed to do this). Add the following section to ch3 | Test Files | phpunit.xml:

    <selenium> <browser name="Firefox" browser="*firefox" /> </selenium>

  8. Open ch3 | Test Files | WebTestCase.php and change TEST_BASE_URL to the URL of our site.

    define('TEST_BASE_URL', 'http://localhost/cbdb/');

  9. Start the Selenium standalone server by opening a terminal window, changing to the directory where you downloaded the standalone server, and running the following command (updated to include the version of the server you downloaded):

    java -jar selenium-server-standalone-<version-number>.jar

  10. Navigate to ch3 | Test Files.

  11. Right-click on the folder named functional and select New | PHP File.

  12. Enter UserTest for the filename and click on Finish.

  13. Input the following contents into the new test file.

    <?php class UserTest extends WebTestCase { protected function setUp() { parent::setUp(); $this->start(); $this->open(''); // login $this->clickAndWait('link=Login'); $this->type('name=LoginForm[username]','admin'); $this->click("//input[@value='Login']"); $this->waitForTextPresent ('Password cannot be blank.'); $this->type('name=LoginForm[password]','test'); $this->clickAndWait("//input[@value='Login']"); // go to users page $this->clickAndWait('link=Users'); } public function testPasswordMatch() { $this->clickAndWait('link=Create User'); $this->type('name=Person[fname]','Func'); $this->type('name=Person[lname]','Test'); $this->type('name=User[username]','functest'); $this->type('name=User[password]','functest'); $this->type('name=User [password_repeat]','nomatchpass'); $this->clickAndWait("//input[@value='Create']"); $this->assertTextPresent ('Password must be repeated exactly.'); $this->assertTextPresent ('Does not meet password requirements.'); $this->assertTextNotPresent ('Password is too short (minimum is 8 characters).'); } public function testPasswordTooShort() { $this->clickAndWait('link=Create User'); $this->type('name=Person[fname]','Func'); $this->type('name=Person[lname]','Test'); $this->type('name=User[username]','functest'); $this->type('name=User[password]','moo'); $this->type('name=User[password_repeat]','moo'); $this->clickAndWait("//input[@value='Create']"); $this->assertTextPresent ('Password is too short (minimum is 8 characters).'); $this->assertTextPresent ('Does not meet password requirements.'); $this->assertTextNotPresent ('Password must be repeated exactly.'); } public function testGoodPassword() { $this->clickAndWait('link=Create User'); $this->type('name=Person[fname]','Func'); $this->type('name=Person[lname]','Test'); $this->type('name=User[username]','functest'); $this->type('name=User[password]','m00!Isay'); $this->type('name=User[password_repeat]','m00!Isay'); $this->clickAndWait("//input[@value='Create']"); $this->assertTextPresent('View User'); } public function testDeleteUser() { $this->clickAndWait ("xpath=(//img[@alt=\"View\"])[2]"); $this->clickAndWait('link=Delete User'); $this->assertConfirmation ('Are you sure you want to delete this item?'); $this->assertTextNotPresent('functest'); } } ?>

  14. Be sure to save the new contents. Then, while viewing the new test file in NetBeans, press Shift + F6 to run the functional test. You should see Selenium and Firefox windows flash on your screen as the tests run.

The tests should complete successfully and confirm that the password validation rules are applied correctly.

Objective Complete - Mini Debriefing

In order to improve our site security, we have added a custom validation rule to the user model. The new rule implements a password strength requirement that we defined, but you can replace this with your own custom definition or an existing library, such as CrackLib. To make sure your new rule is being enforced correctly, and to demonstrate functional testing with Selenium, we added a set of Selenium tests.

Summary

In this project, we have improved user management for our site by replacing the default file- based user management that Yii framework provides with database-stored users. By making this change, we get the bene?t of the web interface to manage our users, instead of having to change the text in a source file. We removed the default User index and replaced it with the Yii-generated admin page, which provides Ajax record searching and quick links to view/update/delete users. We also customized this view to include support for fields from the related table, Person. As a result we can search and sort on fields from Person, as well as User. We improved site security by creating a custom validation rule that enforces some password strength requirements, and we apply this rule only when we need to change the password, not when we are making a change to an existing user.

Resources for Article :


Further resources on this subject:


About the Author :


James R. Hamilton III

James Hamilton has been developing software professionally for 15 years, and is a founding partner of Plum Flower Software, a company that specializes in web and mobile development. He lives in a house full of Linux boxes and cats in Franklin, Tennessee.

Lauren J. O'Meara

Lauren J. O'Meara was lured into the field of computing from being a Mathematics major when she took Jeff Ondich's CS117 Computer Science course at Carleton College in Northfield Minnesota. After completing her degree in Computer Science, she wrote computer security software on both coasts of the US, at the MITRE corporation, and then at OneSecure (later acquired by NetScreen, and then by Juniper Networks). After spending some time gaining a broader understanding of business in the Santa Clara University MBA program, she flung herself into the world of web application development.

Today her web and mobile development consulting company, Plum Flower Software, is based out of Music City, USA.

Books From Packt


CodeIgniter for Rapid PHP Application Development
CodeIgniter for Rapid PHP Application Development

 CouchDB and PHP Web Development Beginner's Guide
CouchDB and PHP Web Development Beginner's Guide

CakePHP Application Development
CakePHP Application Development

PHP Application Development with NetBeans: Beginner's Guide
PHP Application Development with NetBeans: Beginner's Guide

Node Web Development
Node Web Development

PHP and MongoDB Web Development Beginner’s Guide
PHP and MongoDB Web Development Beginner’s Guide

OGRE 3D 1.7 Application Development Cookbook
OGRE 3D 1.7 Application Development Cookbook

CouchDB and PHP Web Development Beginner's Guide
CouchDB and PHP Web Development Beginner's Guide


No votes yet

Post new comment

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
h
C
Q
S
5
5
Enter the code without spaces and pay attention to upper/lower case.
Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software