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.
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).
- 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)
- 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.
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
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:
Connection
- 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.
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.
- 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 .
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).
- 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.
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.
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"); } ?>