*/ ?>
User session management

In this HOWTO, we will show you how to follow the users of your app in their navigation through lists and pages.

HOWTO's context:

The examples exposed here are based on the explanations given in the "Dynamic XML list" HOWTO; if you haven't read it yet, you should do it before you get any further.

In the previous HOWTO, you learned how to generate a list of articles on the fly: any user can see an up-to-date list of all articles existing in the DB.

What we want here is that a user can see a specific list of articles linked to his navigation; for instance, a list of articles that he has put in his shopping cart.
Therefore, the server must identifie the user calling script, in order to, for example:
  • add an article in his shopping cart,
  • display a list of articles currently in his cart
  • display a list of all articles (or of a given category), precising for each of them the eventual quantity already in cart...

Requirements

To start with, you should ahve read previous article: "Dynamic XML list".

To test the sample scripts of this HOWTO, you need a web server with PHP language and MySQL database. Minimal comprehension of PHP and MySQL is required.

You can modify following examples to test them with another language or DB. The only technical constraint is : the produced XML document must be compliant to AppMobile XML format.

If you don't have a server, just type "free hosting php mysql" in your favourite internet search engine and find plenty of solutions that will be more than enough for your testing phase.

Principle

When a user launches an AppMobile application, a unique ID is created and attributed to this instance. This ID will remain identical as long as the user doesn't quit the application.

The application will pass this ID as a parameter to all pages it calls: HTML page, XML list, RSS feed, scripts configured in AppMobile modules...
You just need to recover this parameter server-side to identify the session and link it with the user's data (shopping cart, preferences, account, profile...)

Identify a session

The session identifier is passed in the URL as a GET parameter:
http://mysite.com/myscript.php?id_mvsappmobile=xxx.

To read the value of this parameter in PHP:

<?php
$userid = $_GET['id_mvsappmobile'];
?>

Link a session to a user account

All URLs called from the same instance af an application will be passed the same ID (concretely, all calls from the same phone).

You have to manage three stages with this ID:
  • user log in (ie. create a link between a session ID and a user)
  • connection control for each called URL
  • user log out

User log in

The log in operation is based on a user name (login), a password and the session ID sent to an identification script.

Asking user for identifiers (login/password)

There are two ways of asking the user to enter his login and password:
  • either by inserting a login form in a HTML page anywhere in your app
    For instance, a first "Welcome" tab of type "External HTML page", calling the URL of following logform.php script (cf <form> element) :

    <?php $id_mvs=$_GET['id_mvsappmobile']; include('config.php');?>
    <!DOCTYPE html>
    <html>
    <head>
    	<title>AppMobile documentation sample login form</title>
    	<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    </head>
    	<body>
    		<!-- votre page d'accueil ici -->
    		<form action="<?php echo("$urlprefix/"); ?>login.php" method="post">
    			<input type="text" name="login" value="Login" title="Login"/>
    			<input type="password" name="mdp" value="Password" title="Password"/>
    			<input type='hidden' name='id_mvsappmobile' value='<?php echo($id_mvs); ?>' />
    			<input type="submit" value="Connect" />
    		</form>
    	</body>
    </html>
  • or by using the integrated AppMobile login module.
    When launching the app, it displays a connection screen. Users must log in to go further. His identifiers may be saved locally by the app to allow automatic log in in the future.
    To activate and configure this module, clic on "Next step" in mvsappmobile.com portal until you reach the "Optional modules" section. DetailsModule connexion
    Note. If you activate SHA1 or MD5 crypting in the module configuration, the application will crypt the password before sending it to the server: don't recrypt it server-side on database password comparison.

Sending identifiers

In both cases, identifiers entered by users (login + password) as well as session ID are sent from your app to your connection-script URL.
This URL is the one that you have entered:
  • either in the "action" attribute of the <form> element (connection through HTML form).
    <form action="http://mysite.com/login.php" method="POST">
  • or in the "Your login script URL" field in mvsappmobile.com modules section: DetailsModule connexion

Connection

When the user validates his login identifiers, your app calls the connection-script URL with the 3 following POST parameters:
  • login (connection ID)
  • mdp (password)
  • id_mvsappmobile (session ID)

The connection script must: (1) check if the user with given login and password exists, (2) link this user to the given session ID and (3) delete every eventual already existing connection concerning this user.

From now on, every script called with this session ID can determine which user it is talking to (cf "Access control"section hereunder), as long as the user remains logged in.

Users table

Following information is required to manage connexions: a login, a password and an active session ID for each user.
Table d'utilisateurs

You can store other useful information in this table, depending on your app:as for example: name, address, phone... Or simply the ID of the corresponding record in the customers table (or suppliers, employees, subscriber... to link this user to existing data in your base.
A field describing the user status (free user, subscriber, admin) can allow delivering different levels of contents, or enabling different functionalities, depending on users.

Note:in the preceding example, a primary key was defined, distinct from user identifier. For multiple reasons that exceed the scope of this HOWTO, it's good practice to do so.

This primary key will be used, for instance, in the"shopping cart" HOWTO to set a relation a user and the articles he has selected.

To test the log in / control / log out tests scripts of this HOWTO, you can create the sample table shown above using following MySQL requests (note that the passwords are SHA1 encrypted:

CREATE TABLE IF NOT EXISTS `amdoc_users` (
  `id_user` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `log` varchar(20) NOT NULL,
  `pass` varchar(40) NOT NULL,
  `id_mvsappmobile` varchar(50) DEFAULT NULL,
  PRIMARY KEY (`id_user`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 ;
INSERT INTO `amdoc_users` (`log`, `pass`, `id_mvsappmobile`) VALUES
('Bob', SHA1('MyPass3'), 'disconnected'),
('Tom', SHA1('PassWord'), 'disconnected'),
('Rob', SHA1('PsWd324'), 'disconnected'),
('Sam', SHA1('123'), 'disconnected'),
('Jim', SHA1('azerty'), 'disconnected');

Checking identifiers & connecting

The user entered his login and password, in a HTML form or in the integrated module configured to call folloowing URL:
http://mysite.com/login.php

Here is a sample connection script:

Warning: we decided to make use of the deprecated mysql_* functions in the demo scripts, for larger compatibility reasons. You should NOT run these in production, but download the PDO based modules scripts instead.

<?php
/* Functional demo... Messages and layouts have to be customized! */
include('config.php');//contains data like db connection IDs, base URL, etc.
$login=$_POST['login']; //read parameters -> session id
$passw=sha1($_POST['mdp']); //-> password (encrypted, like in the db)
$id_mvs=$_POST['id_mvsappmobile']; //-> session id
$db_tbl_user="amdoc_users";
$db =  @mysql_connect($dbhost, $dbuser, $dbpassword);
if(!$db) { echo("ConnectProblem");exit; } //MySQL connection problem
if(!@mysql_select_db($dbname)) { echo("DBOpenProblem");exit; } //MySQL connection problem
//Does a user exist with these log/pass? 
$query="select * from $db_tbl_user where `log`='$login' and `pass`='$passw'";
$result = mysql_query($query,$db);
if(!$result) { echo("DBDataProblem");exit; } //MySQL problem
else if(mysql_num_rows($result)==0) { echo("ConnectRefused");exit; } //unexisting user or bad password
else {//there is a user with these log/pass
	//Step 1: disconnect eventual already connected user on this session id. Never 2 users for 1 session.
	$query="update $db_tbl_user set `id_mvsappmobile`='disconnected' where `id_mvsappmobile`='$id_mvs'";
	$result = mysql_query($query,$db);
	if(!$result) { echo("DBDataProblem");exit; } //MySQL problem
	else {
			//Step 2: connect user to account
			$query="update $db_tbl_user set `id_mvsappmobile`='$id_mvs' where `log`='$login' and `pass`='$passw'";
			$result = mysql_query($query,$db);
			if(!$result) { echo("DBDataProblem");exit; } //MySQL problem
	}
}
mysql_close($db);
echo("ok");exit;
?>

Comments in the script give all useful explanations.

Note 1. The error management process of this script is very simplistic: it returns "ok" if the connection succeeded, or a keyword describing the encountered error in all other cases.
  • If you use the integrated connection module, please modify the return values as follows: "1" for success, "0" for bad identifier or password, "-1" for any other failure. The AppMobile connection module will then react consequently.
  • If you prefer to write your own login form in a HTML page, please display an error page instead of returning error codes, and display a welcome page on successful login (for instance, user's dashboard, last orders or messages...)
  • If you choose to use AJAX to post the identifiers entered by the user in your HTML form, then you have to process the return value in Javascript .
Note 2. The above sample code will not work if you activated client-side password crypting in the "optional modules" section of the internet portal: in that case, the password would be crypted twice, see in the first lines of code: $passw=sha1($_POST['mdp']);

Access control

Example: messaging system

Let's imagine an application with a messaging functionality (another example is analysed in the shopping cart management HOWTO).

We want to write the script that generates the list of received messages (for further information on XML lists, please refer to the "Dynamic lists" HOWTO).

Generating this list needs following steps:
  • get the "id_mvsappmobile" parameter that the application passed to the script
  • check if this session is linked to a user account (is the given session ID stored in a user record in the DB?)
    • if not, return a "Please log in" item
    • if yes, search for messages corresponding to this user
      • if messages are found, list them
      • if no mesage found, return a "You've got no message" item

Data

The messaging system is based on following table.
. id_from and id_dest link to the primary key of the user accounts table (the "amdoc_users" table described above).
. opened is used to flag the messages that have already been read; this will allow displaying new and opened messages with different pictures.
. deleted is used to flag deleted messages,rather than permanently deleting them; this allows to display a list of deleted messages (a kind of virtual "thrash" folder) and eventually to recover them.

Table d'utilisateurs

Requests for creating the table and inserting some sample messages:

CREATE TABLE IF NOT EXISTS `amdoc_messages` (
  `id_msg` int(11) NOT NULL AUTO_INCREMENT,
  `subj` varchar(150) DEFAULT '',
  `id_from` int(11) DEFAULT '0',
  `id_dest` varchar(50) DEFAULT '0',
  `date` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
  `text` varchar(1000) DEFAULT '',
  `opened` tinyint(4) NOT NULL DEFAULT '0' COMMENT 'nonzero if opened',
  `deleted` tinyint(4) NOT NULL DEFAULT '0' COMMENT 'nonzero if deleted',
  PRIMARY KEY (`id_msg`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=4 ;
INSERT INTO `amdoc_messages` (`id_msg`, `subj`, `id_from`, `id_dest`, `date`, `text`, `opened`, `deleted`) VALUES
(1, 'Just a test', 3, '4', '2014-07-22 12:25:09', 'Hi,\r\nThis is my first message, just a test, tell me if it works!', 1, 0),
(2, 'Got it', 4, '3', '2014-07-22 13:41:26', 'Hello,\r\nJust got your message,\r\nWorks great ;-)', 1, 0),
(3, 'Thanks', 3, '4', '2014-07-22 14:46:07', 'ok, thanks', 0, 0);

Request

Based on this sample data, let's write the script creating the list of messages. The script is very simple, only the SQL query requires some explanations.

In the messages table, each record is linked to a sender (key id_from) and to a recipient (key id_dest) which refer to twe entries of the same users table.
That's why we have to joint the messages table twice to the users table:
  • a first time (FROM `$db_tbl_msg` m, `$db_tbl_user` t, where "t" means "to") to identify the currently connected user (WHERE t.id_mvsappmobile='$id_mvs'), filter messages addressed to this user and m.id_dest=t.id_user and read the contents of these messages (SELECT m.id_msg, m.subj, m.`date`, m.opened
  • a second time (FROM `$db_tbl_msg` m, `$db_tbl_user` f, where "f" means "from") to identify the sender (WHERE f.id_user=m.id_from) and read his name (SELECT f.log as exp).

We shall not forget to keep only messages that haven't been deleted (and m.deleted=0)... which gives:

SELECT m.id_msg, m.subj, m.`date`, m.opened, f.log as exp
FROM `$db_tbl_msg` m, `$db_tbl_user` t,`$db_tbl_user` f
WHERE t.id_mvsappmobile='$id_mvs' and m.id_dest=t.id_user and f.id_user=m.id_from and m.deleted=0

Script: messages list

<?php
include('config.php');//données de config, notamment $dbhost,$dbuser,$dbpassword,$dbname pour accéder à la base 
$id_mvs=$_GET['id_mvsappmobile']; //récupération del'id de session passé en paramètre
$xml="<?xml version='1.0' encoding='UTF-8'?>\r\n<items version='1.0' type='1' reload='1' locate='0'>";//le XML de la liste à afficher
header ("Content-Type:text/xml; charset=utf-8");//entête http
function add_item(&$xmlstring, $typ, $img, $tx1, $tx2, $tx3, $link) {
	$xmlstring.="
	<item type='$typ'> 
		<img>$urlprefix/$img</img>
		<txt1>$tx1</txt1>
		<txt2>$tx2</txt2>    
		<txt3>$tx3</txt3>
		<link type='1' url='$link' title='$tx1' />
	</item>";
}
function quit_script(&$xmlstring, $errorMsg, $pic) {
	add_item($xmlstring, 1, $pic, $errorMsg, "", "", "");
	echo($xmlstring."\r\n</items>");
	exit;
}
$db =  @mysql_connect($dbhost, $dbuser, $dbpassword);
if(!$db) { quit_script($xml,"Pas de connexion","pic/noConnec.png"); } //Problème connexion serveur MySQL
if(!@mysql_select_db($dbname)) { quit_script($xml,"Pas de connexion","pic/noConnec.png"); } //Problème sélection base MySQL
$query="select id_user from $db_tbl_user where id_mvsappmobile='$id_mvs'";
$result = @mysql_query($query,$db);
if (!$result) { quit_script($xml,"Pas de connexion","pic/noConnec.png"); } //Problème MySQL
else {//data was found
	if(mysql_num_rows($result)!=1) { quit_script($xml,"Merci de vous identifier","pic/noSession.png"); }//no connected user for this session
	else {//user is connected
		$query = "SELECT m.id_msg, m.subj, m.`date`, m.opened, f.log as exp
		FROM `$db_tbl_msg` m, `$db_tbl_user` t,`$db_tbl_user` f 
		WHERE t.id_mvsappmobile='$id_mvs' and m.id_dest=t.id_user and f.id_user=m.id_from and m.deleted=0";
		$result = @mysql_query($query,$db);
		@mysql_close($db);
		if(!$result) { quit_script($xml,"Pas de connexion","pic/noConnec.png"); } //Problème MySQL
		else if (mysql_num_rows($result) == 0) { add_item($xml, 1, "pic/noMsg.png", "Aucun message", "", "", ""); }//there's no message
			else {//there are messages
				while ($l = mysql_fetch_assoc($result)) {
					$im=($l['opened']=="1" ? "pic/openedMsg.png" : "pic/newMsg.png");
					add_item($xml, 1, $im, $l['subj'], "From: ".$l['exp'], date('d/m/Y',strtotime($l['date'])), "$urlprefix/msg_show.php?msg=".$l['id_msg']."&id_mvsappmobile=$id_mvs");
				}
			}
		echo ($xml."\r\n</items>");
	}
}
?>

Log out

To disconnect a user, you just have to cut the link between his session ID and his account.

update `amdoc_users` set `id_mvsappmobile`='disconnected'
where `id_mvsappmobile`='{id de session}'

That's all...

<?php
/* Functional demo... Messages and layouts have to be customized! */
include('config.php');//contains data like db connection IDs, base URL, etc.
$id_mvs=$_GET['id_mvsappmobile']; //read parameters -> session id
$db =  @mysql_connect($dbhost, $dbuser, $dbpassword);
if(!$db) { echo("ConnectProblem");exit; } //MySQL connection problem
if(!@mysql_select_db($dbname)) { echo("DBOpenProblem");exit; } //MySQL db selection problem
//déconnexion 
$query="update $db_tbl_user set `id_mvsappmobile`='disconnected' where `id_mvsappmobile`='$id_mvs'";
$result = mysql_query($query,$db);
if(!$result) { echo("DBDataProblem");exit; } //MySQL problem
$nbrows=mysql_affected_rows($db);
mysql_close($db);
if($nbrows==0) { echo("UnexistingSession");exit; } //MySQL problem
else if($nbrows>1) { echo("SeveralSessionsDisconnected");exit; } //This should never happen.
//The login script must refuse connection of new user on existing session id, OR delete existing connection before connecting new.
else { echo("DisconnectOk"); }
?>