40

Manage a community of users

Create site15 by copying site14.

  1. /cms
    1. ...
    2. site14
    3. site15

In this chapter, we are going to manage a community of users in a DB and grant them roles.

To test the result online, enter http://www.frasq.org/cms/site15 in the address bar of your navigator. Connect as foobar with the password f00bar. A green button with a check appears in the banner of the home page. If you click it, the content of the page is transmitted to the W3C's validator. Disconnect and reconnect as barfoo with the password barf00. The link to the W3C isn't displayed.

Start the command processor of MySQL and enter the site's DB:

$ mysql -u root -p
mysql> use frasqdb2;

NOTE: Use phpMyAdmin for more comfort.

Add the table user to the site's DB:

CREATE TABLE USER (
  user_id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
  name VARCHAR(40) NOT NULL,
  `password` VARCHAR(32) NOT NULL,
  mail VARCHAR(100) DEFAULT NULL,
  created datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
  `access` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
  locale enum('fr','en') NOT NULL DEFAULT 'fr',
  active tinyint(1) NOT NULL DEFAULT '1',
  banned tinyint(1) NOT NULL DEFAULT '0',
  PRIMARY KEY (user_id),
  UNIQUE KEY name (name),
  UNIQUE KEY mail (mail)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8;

The primary key user_id is also the user's number. name and mail are unique keys which allow the identification of a user. password contains the user's password encrypted in MD5. created keeps the creation date and time of the account. access records the date and time of the last connection to the account. locale gives the user's preferred language among the ones managed by the site. active indicates if the account is accessible. banned signals a user who is undesirable.

Create a user foobar with the password f00bar and a user barfoo with the password barf00:

INSERT INTO USER (name, password, mail, created)
VALUES ('foobar', MD5('f00bar'), 'foobar@localhost', NOW());
INSERT INTO USER (name, password, mail, created)
VALUES ('barfoo', MD5('barf00'), 'barfoo@localhost', NOW());

Notice that the passwords are encoded in MD5. IMPORTANT: Never keep passwords in clear text.

Add the tables role and user_role to the site's DB:

CREATE TABLE `role` (
  role_id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
  name VARCHAR(40) NOT NULL,
  PRIMARY KEY (role_id),
  UNIQUE KEY name (name)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8;

role is nothing more than a list of names.

CREATE TABLE user_role (
  user_id INT(10) UNSIGNED NOT NULL DEFAULT '0',
  role_id INT(10) UNSIGNED NOT NULL DEFAULT '0',
  PRIMARY KEY (user_id,role_id),
  KEY `role` (role_id)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

user_role associates a user to a role. A user can have several roles.

Define the roles administrator, writer, reader and moderator:

INSERT INTO `role` (name) VALUES ('administrator');
INSERT INTO `role` (name) VALUES ('writer');
INSERT INTO `role` (name) VALUES ('reader');
INSERT INTO `role` (name) VALUES ('moderator');

Associate the user foobar to the roles administrator and writer:

INSERT INTO user_role (user_id, role_id) VALUES (1, 1);
INSERT INTO user_role (user_id, role_id) VALUES (1, 2);

Check that you can list the role names of a user:

mysql> SELECT u.name AS user_name, r.name AS role_name FROM user u
JOIN user_role ur ON ur.user_id=u.user_id
JOIN role r ON r.role_id=ur.role_id
WHERE ur.user_id=1;
+-----------+---------------+
| user_name | role_name     |
+-----------+---------------+
| foobar    | administrator |
| foobar    | writer        |
+-----------+---------------+
2 rows in set (0.00 sec)
mysql> quit

Edit the file user.inc in the folder models and add the following functions:

  1. function user_get($user_id) {
  2.     if (!is_numeric($user_id)) {
  3.         return false;
  4.     }
  5.  
  6.     $tabuser=db_prefix_table('user');
  7.  
  8.     $sql="SELECT name AS user_name, password AS user_password, mail AS user_mail, UNIX_TIMESTAMP(created) AS user_created, UNIX_TIMESTAMP(access) AS user_access, locale AS user_locale, active AS user_active, banned AS user_banned FROM $tabuser WHERE user_id=$user_id LIMIT 1";
  9.  
  10.     $r = db_query($sql);
  11.  
  12.     return $r ? $r[0] : false;
  13. }

user_get returns an array with the fields user_name user_password, user_mail, user_created, user_access, user_locale, user_active and user_banned of the user whose number is $user_id or false if $user_id isn't a valid user number.

  1. function user_get_role($user_id) {
  2.     if (!is_numeric($user_id)) {
  3.         return false;
  4.     }
  5.  
  6.     $tabrole=db_prefix_table('role');
  7.     $tabuserrole=db_prefix_table('user_role');
  8.  
  9.     $sql="SELECT r.name AS role_name FROM $tabuserrole ur JOIN $tabrole r ON r.role_id=ur.role_id WHERE ur.user_id=$user_id";
  10.  
  11.     $r = db_query($sql);
  12.  
  13.     if (!$r) {
  14.         return false;
  15.     }
  16.  
  17.     $role=array();
  18.  
  19.     foreach ($r as $v) {
  20.         $role[] = $v['role_name'];
  21.     }
  22.  
  23.     return $role;
  24. }

user_role returns the list of the roles of the user $user_id or false if $user_id isn't a valid user number.

  1. function user_find($login) {
  2.     $sqllogin=db_sql_arg($login, true);
  3.  
  4.     $tabuser=db_prefix_table('user');
  5.  
  6.     $sql="SELECT user_id FROM $tabuser WHERE name=$sqllogin OR mail=$sqllogin LIMIT 1";
  7.  
  8.     $r = db_query($sql);
  9.  
  10.     return $r ? $r[0]['user_id'] : false;
  11. }

user_find checks if a user whose name or email address is $login is defined in the DB. user_find returns the user's unique number or false in case of failure.

  1. function user_login($login, $password) {
  2.     $user_id = user_find($login);
  3.  
  4.     if (!$user_id) {
  5.         return false;
  6.     }
  7.  
  8.     $r = user_get($user_id);
  9.  
  10.     if (!$r) {
  11.         return false;
  12.     }
  13.  
  14.     extract($r);    /* user_name user_password user_mail user_created user_access user_locale user_active user_banned */
  15.  
  16.     if (!$user_active or $user_banned) {
  17.         return false;
  18.     }
  19.  
  20.     $password=md5($password);
  21.  
  22.     if ($password != $user_password) {
  23.         return false;
  24.     }
  25.  
  26.     $now=time();
  27.  
  28.     $user = array();
  29.     $user['id'] = $user_id;
  30.     $user['name'] = $user_name;
  31.     $user['mail'] = $user_mail;
  32.     $user['locale'] = $user_locale;
  33.     $user['created'] = (int)$user_created;
  34.     $user['access'] = $now;
  35.  
  36.     $r = user_get_role($user_id);
  37.  
  38.     $user['role'] = $r;
  39.  
  40.     $tabuser=db_prefix_table('user');
  41.  
  42.     $sql="UPDATE $tabuser SET access=FROM_UNIXTIME($now) WHERE user_id=$user_id LIMIT 1";
  43.  
  44.     db_update($sql);
  45.  
  46.     return $user;
  47. }

user_login obtains the number of the user whose name or email address is $login with user_find, then all the properties of the account with user_get. If $login isn't the name or the email address of a user or if $password doesn't match the password or if the account is inactive or blocked, user_login returns false. If the connection is accepted, $login writes down the date and the time in the DB before returning an array with the fields id, name, mail, locale, created and access extracted from the array returned by user_get and the field role containing the list of roles returned by user_get_role.

Notice that the interface to the user_login function hasn't changed. Therefore you don't need to modify the form of the identification page. Enter http://localhost/cms/site15/en/user in the address bar of your navigator and check that everything is like before.

Add the file userhasrole.php in the folder library with the following content:

  1. /cms/site15
    1. library
      1. userhasrole.php
  1. function user_has_role($role) {
  2.     return isset($_SESSION['user']) and $_SESSION['user']['role'] and in_array($role, $_SESSION['user']['role']);
  3. }

user_has_role returns true if the user is connected and if $role is one of her roles, or false otherwise.

Modify the banner function which builds the block of the banner of the site in the file banner.php in the folder blocks :

  1. require_once 'userhasrole.php';

Loads the function user_has_role.

  1.     $menu=$contact=$login=$logout=$validate=false;
  2.     $languages=false;
  3.     $user_page=$contact_page=$nobody_page=$validate_page=false;
  4.  
  5.     $is_identified = user_is_identified();
  6.     $is_writer = user_has_role('writer');

Initializes the variables $validate and $validate_page. Sets $is_writer to true if the user has the role writer, to false if not.

  1.                 case 'validate':
  2.                     if ($param) {
  3.                         if ($is_writer) {
  4.                             $validate_page=$param;
  5.                             $validate=true;
  6.                         }
  7.                     }
  8.                     break;

If 'validate' is in $components, if $param isn't false and if $is_writer is true, assigns the value associated to the component to $validate_page and sets $validate to true.

  1.     if ($logout or $contact) {
  2.         $menu = view('bannermenu', $lang, compact('contact', 'contact_page', 'validate', 'validate_page', 'logout', 'nobody_page', 'login', 'user_page'));
  3.     }

Passes the parameters $validate and $validate_page to the view.

Modify the home action in the file home.php in the folder actions to pass the link to the W3C to the banner block:

  1.     $validate=url('home', $lang);
  2.  
  3.     $banner = build('banner', $lang, compact('languages', 'contact', 'account', 'validate'));

Assign the URL of the home page to $validate and passes the parameter to banner.

Add the link to the validator to the view of the menu of the banner in the files views/en/bannermenu.phtml for the English version and views/fr/bannermenu.phtml for the French version:

  1. <?php if (isset($validate) and $validate): ?>
  2. <li><a id="validate" href="http://validator.w3.org/check?uri=<?php echo $base_root . $validate_page; ?>" target="validator.w3.org" title="Validate"><span>Valider</span></a></li>
  3. <?php endif; ?>
  1. <?php if (isset($validate) and $validate): ?>
  2. <li><a id="validate" href="http://validator.w3.org/check?uri=<?php echo $base_root . $validate_page; ?>" target="validator.w3.org" title="Valider"><span>Valider</span></a></li>
  3. <?php endif; ?>

Modify the style sheet to display a button instead of a link to the W3C:

  1. #bannermenu #validate {width:24px;height:24px;float:left;margin-right:6px;background:transparent url(../buttons/check.png) no-repeat center center;}

Copy the icon in the folder buttons:

  1. /cms/site14
    1. buttons
      1. check.png

Enter http://localhost/cms/site15 in the address bar of your navigator. Connect as foobar with the password f00bar. Check that the validation button is displayed on the home page. Disable the CSS to evaluate the quality of the generated document. Move the mouse over the button to control the URL. If your site is accessible on the web, click on the button to validate the content of the page. Connect as barfoo with the password barf00. Check that the validation button isn't displayed on the home page.

Comments

Your comment:
[p] [b] [i] [u] [s] [quote] [pre] [br] [code] [url] [email] strip help 2000

Enter a maximum of 2000 characters.
Improve the presentation of your text with the following formatting tags:
[p]paragraph[/p], [b]bold[/b], [i]italics[/i], [u]underline[/u], [s]strike[/s], [quote]citation[/quote], [pre]as is[/pre], [br]line break,
[url]http://www.izend.org[/url], [url=http://www.izend.org]site[/url], [email]izend@izend.org[/email], [email=izend@izend.org]izend[/email],
[code]command[/code], [code=language]source code in c, java, php, html, javascript, xml, css, sql, bash, dos, make, etc.[/code].