Location: PHPKode > scripts > QCM Class > qcm-class/QCM.php
<?
class qcm {

	//   QCM CLASS
	//   ---------
	//   Author  : Copyright Pierre FAUQUE, hide@address.com, feb 2002 - avr 2009.
	//   Usage   : Read, display and correct a questionnaire online.
	//   QCM     : French acronym. Stands for "Questions with Multiple Choices".
	//   License : Aladdin Free Public License (provided with this file)
	//   Version : 2.16
	//
	//   <DELETE>
	//
	//   These comments are users comments. At the bottom of this file class, there
	//   are technical comments. All comments are inserted in this file to avoid
	//   separate files. I warmly recommand you to delete all lines between <DELETE>
	//   ans </DELETE> (these tags included) once you have printed them off.
	//
	//
	//   USER COMMENTS
	//   -------------
	//
	//   This class read a text file named QCM file. This QCM file is identified by
	//   a number. If the identifier of the QCM is the number 3, the QCM filename
	//   will be "qcm3.txt". This QCM file is divided in two parts : the header and
	//   the test itself. These two parts are separated by a dot, alone on a line.
	//   In each part, every line beginning by the character ; or the character # is
	//   a comment and will be ignored.
	//
	//   The header contains several lines. The syntax of these lines is "key=value".
	//   If an expected "key=value" is not present in the header, a default value will
	//   be applied to the missing key. Sometimes there are several elements in the
	//   value side. In this case, the elements are comma separated.
	//   The header is explained below.
	//
	//   The test itself is composed by several items and are blank line separated.
	//   Each item belongs to a type among several. There are 6 types of questions :
	//
	//      - Item type 0: Multiple proposals but unique response.
	//      - Item type 1: Multiple proposals and multiple responses.
	//      - Item type 2: Proposals to be ordered.
	//      - Item type 3: Proposals to be associated with other proposals.
	//      - Item type 4: Proposal completion by element selection in a list.
	//      - Item type 5: Proposals having to be completed with a word(s)
	//
	//   The header
	//   ----------
	//
	//   - author:      Name and email of the author of QCM.
	//   - mailto:      Teacher mail address (to send the result by mail).
	//   - smsto:       Teacher phone number (to send the result by SMS).
	//   - starton:     On 'starton' (date and hour) the test begins (is reachable).
	//   - stopon:      On 'stopon' (date and hour) the test ends (is unreachable).
	//   - mintime:     Low limit of the normal duration of the test
	//   - maxtime:     High limit of the normal duration of the test
	//   - categories:  Categories comma separated (Geography,Physic,Mathematics,etc...).
	//   - insertpoint: Character to indicate where a list will be inserted. (default is '|')
	//   - shownum:     Either 1 or 0. Show or not the number of each question on display.
	//   - showsender:  Either 1 or 0. Show the address of the sender (IP or hostname) in mails
	//   - showserver:  Either 1 or 0. Show the address of the server (IP and hostname) in mails
	//   - showtitle:   Either 1 or 0. Show or not in the page the title of the questionnaire.
	//   - checkvers:   Either 1 or 0. Check or not for a new version.
	//   - flagnew:     String or character to indicate (if checkvers=1) that a new version exists (web page)
	//   - drawchart:   "none","pie","lines" or "histo". Draw or not a chart.
	//   - imgtype:     "png", "jpeg", "jpg" or "gif". Graphic type. Default is "png".
	//   - logo:        Define a logo and its position (top,bottom,left,center,right)
	//   - language:    Language to be used (national domains: fr, en, it, es, ...). Default is fr
	//   - charset:     Define the charset to use in mails. Default is "iso-8859-1"
	//   - indentsize:  Number of "&nbsp;" before each proposition.
	//   - bgcolor:     Define the background color (default is #D0D0D0)
	//   - askfor:      "none, "mail", "phone" or "mail,phone" to ask for or not these infos.
	//   - ponderation: Either 1 or 0. Use nb of correct answers (0) or points instead (1).
	//   - details:     "short", "medium", "large", "full". "large" (responses), "full" (correction).
	//   - drawsheet:   Either 1 or 0. Draw or not a statistics sheet.
	//   - thresold:    Level under which the test isn't good. Use . for decimal notation.
	//   - goal:        Goal of the questionnaire: test or sample survey ("test", "opinion")
	//   - title:       Title of the QCM.
	//   - version:     Version of the QCM.
	//   - release:     Month and year of the QCM creation (mmm aaaa).
	//   - org:         Organization.
	//   - site:        Web site where the test is.
	//   - urlback:     URL where we go after the test.
	//   - sqlserver:   Host on which the SQL server is and its port.
	//   - save:        Save media ('none', 'file', 'mysql' or 'postgresql').
	//                  if file: save=file,filename (the file must have write rights).
	//                  if sql:  save=server type,database,table,login,password
	//   - userlist:    'none','file' (for the moment)
	//                  if none: the user have to provide his name
	//                  if file: userlist=file,filename (the user have to select his name in a combo).
	//                  database: userlist=server type,database,table,login,password (in a future version)
	//   - .            A dot alone on the line. This mark ends the header.
	//
	//   Except the point which must be on the last line of the header, these lines
	//   can be written in any order.
	//
	//   If a user list is used
	//   ----------------------
	//   The parameter is 'userlist=file,filename' (ex: userlist=file,usernames.txt)
	//   The file which holds the users names is a text file. The format of this file is:
	//   Last name;First name;Gender
	//   Example:
	//   +--------------------
	//   | DUPONT;Marie-Claude;Miss
	//   | DUVAL;Pierre;M.
	//   | HOAREAU;Marianne;Ms
	//   | ...
	//   +--------------------
	//
	//   The test itself
	//   ---------------
	//
	//   Each item is composed with several lines :
	//
	//   line 1: properties of the item.
	//   line 2: text of the question.
	//   line 3 and next lines: proposals according to the item type.
	//   a blank line ends each item.
	//
	//   > line 1: properties of the item.
	//
	//   The item properties line is divided in four fields and are semicolon separated :
	//   "item type ; categorie ; correct(s) response(s) ; number of points" (without space)
	//   When several responses are expected they are comma separated.
	//   Examples:
	//   0;;1;1              => Type = 0 ; Not categorized ; Correct resp. = 1 ; 1 pt
	//   1;1;1,2,5;2         => Type = 1 ; Category = 1 ; Correct resp. = 1,2,5 ; 2 pts
	//   2;3;6,1,3,2,5,4;1   => Type = 2 ; Category = 3; Correct resp. = 6,1,3,2,5,4 ; 1 pt
	//   3;0;4,1,3;4         => Type = 3 ; Not categorized ; Correct resp. = 4,1,3 ; 4 pts
	//   4;;3;2              => Type = 4 ; Not categorized ; Correct resp. = 3 ; 2 pts
	//   5;4;mysql;2         => Type = 5 ; Category = 4 ; Correct resp. = mysql ; 2 pts
	//   2;;;                => Type = 2 ; not categorized ; no correct response ; no point
	//                          This is caracteristic if the goal of the questionnaire is an inquiry.
	//
	//   > line 2: text of the question.
	//
	//   > other lines (3 and next): proposals according to the item type
	//     NB: All lists are made with several proposals comma separated.
	//
	//   - type 0 or 1 = one proposal by line.
	//   - type 2      = list of proposals to be ordered (line 3).
	//                   they will be displayed in combo lists.
	//   - type 3      = first list of proposals (line 3). Displayed as text on the web page.
	//                   second list of proposals (line 4). Displayed in combo lists.
	//   - type 4      = proposal to be completed with a selected element in a list (line 3).
	//                   list of elements to complete the proposal (line 4).
	//   - type 5      = proposal to be completed with a word(s).
	//
	//   > last line: a blank line ends each item (not necessary for the last item).
	//
	//   Example
	//   -------
	//
	//   +------- QCM file ----------------------
	//   |title=General test
	//   |version=1
	//   |release=oct 2004
	//   |; indentsize=10
	//   |; bgcolor=#FFFFFF
	//   |goal=test
	//   |sqlserver=sql.free.fr:3306
	//   |save=mysql,CommonBase,RTests,pierre,Kj9RuZ4H
	//   |userlist=none
	//   |; insertpoint=|
	//   |author=Pierre FAUQUE,hide@address.com
	//   |org=Webdev Tests
	//   |site=http://www.fr-webdev.net/test/
	//   |urlback=http://www.fr-webdev.net
	//   |mailto=hide@address.com
	//   |; language=en
	//   |; smsto=0683485270
	//   |; charset=iso-8859-1
	//   |starton=08/05/2004 09:00
	//   |stopon=18/05/2004 09:00
	//   |mintime=00:15:00
	//   |maxtime=00:30:00
	//   |logo=top,bottom,http://www.website.com/img/logo.jpg,center
	//   |shownum=1
	//   |showtitle=1
	//   |showsender=1
	//   |showserver=1
	//   |; checkvers=0
	//   |; flagnew=*
	//   |askfor=name,mail
	//   |ponderation=1
	//   |; details=full
	//   |drawsheet=1
	//   |thresold=60.5
	//   |; imgtype=jpeg
	//   |drawchart=pie
	//   |categories=Mathematics,Internet,Geography
	//   |.
	//   |2;3;1,3,4,2;1
	//   |Order the countries in following combo lists from the smaller to the greater
	//   |Ireland,Australia,England,France
	//   |
	//   |3;1;4,2,1;2
	//   |In front of each proposal, choose in the combo list the correct response
	//   |Square root of 2,Square root of 3,PI
	//   |3.14,1.732,1.325,1.414
	//   |
	//   |0;2;1;1
	//   |Internet exists since 1964
	//   |True
	//   |False
	//   |
	//   |4;2;3;1
	//   |Select the good element in the list to complete correctly the proposal
	//   |An | server runs on the port 22.
	//   |ftp,http,ssh,smtp,pop,irc,
	//   |
	//   |5;2;mysql;1
	//   |# The size of the input text is 5 ( => |5| )
	//   |Complete the following sentence with the correct word
	//   |A |5| server waits connections on the port 3306
	//   |
	//   |1;2;2,4,7;1
	//   |# Other proposals are not network protocols but application protocols
	//   |Check for network protocols in the following list
	//   |pop
	//   |tcp/ip
	//   |http
	//   |netbeui
	//   |nntp
	//   |smtp
	//   |ipx/spx
	//   +--------------------------------------
	//
	//   +---------------------------------------------------------+
	//   | NOTE:                                                   |
	//   | In this distribued version, the sendSMS() method is not |
	//   | available because the code of this method is specially  |
	//   | written for my professional intranet.                   |
	//   +---------------------------------------------------------+
	//
	//   How to use this class. Example
	//   ------------------------------
	//
	//   "index.php" is the PHP file to display the QCM file number 17.
	//   "result.php" is the PHP file to correct this questionnaire and show the result.
	//
	//   Essential content of "index.php" :
	//
	//       ...
	//       echo "<form method='post' name='qcm' action='result.php'>";
	//       require("QCM.php");        // Insert the QCM Class
	//       $test = new qcm(17);       // New object. QCM file to be read : qcm17.txt
	//       $test->display();          // Display the questionnaire
	//       echo "</form>";
	//       ...
	//
	//   Essential content of "result.php" :
	//
	//       ...
	//       require("QCM.php");        // Insert the QCM Class
	//       $test = new qcm(17);       // New object. QCM file to be read : qcm17.txt
	//       $test->correction();       // Do all (correction, graph, mail, etc...)
	//       ...
	//
	//   You can also use only one file "test.php" as below :
	//
	//       $idQCM = 3;
	//       require("QCM.php");
	//       $test = new qcm($idQCM);
	//       ...
	//       if(!$_POST['submit']) {
	//          echo "<form method='post' name='qcm' action='".$_SERVER['PHP_SELF']."'>";
	//          $test->display();
	//          echo "</form>";
	//       else {
	//          $test->correction();
	//       }
	//       ...
	//
	//   Some methods can be useful :
	//       To display some information or report a bug    : $test->info("bug|whish|about|copyright");
	//       To show variables while working on the class   : $test->showVars();
	//       To show questions and the given responses      : $test->showResponses()
	//       To define a text to be displayed after inquiry : $test->msgEnd("Some text...")
	//
	//   Exceptionnaly, you can create a new object with "null" as parameter (just for the about() method)
	//   instead of the number of the QCM file, and use the about() method :
	//
	//      $test = new qcm("null");  or  $test = new qcm();
	//      $test->info("about");
	//
	//
	//   URL : http://www.fr-webdev.net/ (headings: "scripts", "la classe qcm")
	//   ---
	//
	//   download:     "téléchargement"
	//   wishes list:  "wishes list"
	//   report a bug: "signaler un bug"
	//
	//   </DELETE>
	//
	// =======================================================
	// ATTRIBUTES OF THIS CLASS
	// All texts are written here. If you want to use this class with another language,
	// you have to translate the provided file 'texts.language.php' saving it using the
	// country domains in its name: texts.en.php, texts.it.php, texts.nl.php, and so on...
	// The texts below are default texts.
	// ----------------------------------------------------------
	// Texts (all texts begin by $t_*)
	var $t_begin        = array("Le test débute le ","Le questionnaire débute le ");  // test or inquiry
	var $t_end          = array("Le test s'est terminé le ","Le questionnaire s'est terminé le ");
	var $t_between      = array("Période de validité du test ","Période de validité du questionnaire ");
	var $t_nopoints     = "Vous n'avez obtenu aucune bonne réponse";
	var $t_bt_send      = "Envoyer";
	var $t_bt_clear     = "Effacer";
	var $t_youknow      = "Vos acquis";
	var $t_sections     = "Rubriques";
	var $t_statistics   = "Statistiques";
	var $t_desactivate  = "Désactivé";
	var $t_nbpoints     = "Nombre total de points acquis";
	var $t_point        = array("point","points");                  // singular, plural
	var $t_response     = array("bonne réponse","bonnes réponses"); // singular, plural
	var $t_nbquestions  = "Nombre total de questions";
	var $t_nbcresponses = "Nombre de bonnes réponses";
	var $t_nbiresponses = "Nombre de mauvaises réponses";
	var $t_nbnresponses = "Nombre de non réponses";
	var $t_noresponse   = "Pas de réponse";
	var $t_playtime     = array("Durée du test","Durée du questionnaire");
	var $t_competence   = "Compétence générale";
	var $t_testdate     = array("Date du test","Date du questionnaire");
	var $t_smssent      = "SMS envoyé au";
	var $t_smsduracy    = "Duree";
	var $t_smsnbq       = "nb Quest.";
	var $t_smsca        = "Bonnes rep.";
	var $t_smsia        = "Mauv. rep.";
	var $t_smsnote      = "Note";
	var $t_yourresults  = "VOS RESULTATS";
	var $t_subject      = array("Test en ligne","Questionnaire en ligne");
	var $t_anon         = "Anonyme";
	var $t_student      = array("NOM, Prénom, tel","NOM Prénom, tel");
	var $t_mail         = "e.mail";
	var $t_on           = "sur";
	var $t_ok           = "OK";
	var $t_ko           = "KO";
	var $t_quit         = "Quitter";
	var $t_msgend       = "Merci de votre participation."; // if inquiry, displays that message.
	var $t_qcmerror     = "Erreur classe QCM";
	var $t_reportbug    = "Signaler un bug";
	var $t_wish         = "Demander une fonctionnalité";
	var $t_newvers      = "Une nouvelle version de la classe QCM existe à";
	var $t_yourreplies  = "VOS REPONSES";
	var $t_correction   = "CORRECTION";

	// Errors messages
	var $error          = array (

		// From 100 up to 199: general errors
		100 => "La classe 'qcm' ne fonctionne qu'à partir de PHP version 4.",
		199 => "Erreur inconnue.",

		// From 200 up to 299: database errors
		200 => "Le serveur de bases de données n'est pas accessible.",
		201 => "La base de données n'est pas accessible.",
		202 => "Erreur dans la requete SQL.",
		203 => "Connexion à la base de données impossible.",
		299 => "Erreur SQL inconnue."

		// From 300 up to 399: not yet defined.
		// etc...

	);
	// End of texts. Eventual translation stops here
	// --------------------------------------------------------------------------

	// QCM class attributes
	var $qcmver     = "2.16";
	var $qcmrelease = "03 avr 2009";
	var $qcmauthor  = "Pierre FAUQUE";
	var $qcmemail   = "hide@address.com";
	var $qcmsite    = "http://www.fr-webdev.net";
	var $qcmurl     = "http://www.fr-webdev.net/dev/cqcm";
	var $qcmdnl     = "http://www.fr-webdev.net/index.php?p=cqcm.dnl.php";
	var $qcmbug     = "http://www.fr-webdev.net/index.php?p=cqcm.bug.php";
	var $qcmwish    = "http://www.fr-webdev.net/index.php?p=cqcm.wl.php";

	// Several attributes used while working
	var $bgcolor = "#D0D0D0";     // Default background color.
	var $grext = "png";           // Graphic extension ("gif", "jpeg", "jpg" or "png").
	var $indent = "";             // Indentation string for proposals
	var $indentsize = 8;          // Size of the indentation (space number)
	var $insertpoint = "|";       // Special character where insert a list or an input text
	var $itq = 0;                 // QCM Type Index. 0: test, 1: inquiry
	var $mailsign = "";           // Signature of the mails
	var $curver = 2016;           // Current version (This script: (ver maj*1000) + ver min)
	var $refver = 0;              // Reference version number (on the main website)
	var $refrelease = "";         // Releasqe of the eventual new version (on the main website)
	var $newver = "";             // Number of the eventual new version (on the main website)

	// QCM properties read from the QCM file
	var $author = "";             // Name of the author of QCM.
	var $email = "";              // Email address of the author of QCM.
	var $org = "";                // The organization.
	var $site = "";               // Web site on which the QCM is.

	var $file = "";               // QCM filename to be read. (verifier si utile)
	var $title = "";              // Title of the QCM.
	var $goal = "test";           // Goal of the questionnaire ("test", "opinion").
	var $starton = 0;             // Date and hour of the beginning of the test (timestamp).
	var $stopon = 0;              // Date and hour of the end of the test (timestamp).
	var $mintime = "";            // Low limit for the normal duration
	var $maxtime = "";            // High limit for the normal duration
	var $showtitle = 0;           // The title of this QCM is used in H1 tags.
	var $shownum = 0;             // Show the number of each item.
	var $showsender = 0;          // Show or not the student address
	var $showserver = 0;          // Show or not the server address
	var $checkvers = 0;           // Check or not for a new version of the QCM Class
	var $flagnew = "*";           // Define the string or the character used if a new version exists (*)
	var $logopos = 0;             // Position of the logo (byte: "---TBLCR" top, bottom, left, center, right)
	var $logosrc = "";            // Filename or URL of the logo
	var $version = "";            // QCM version.
	var $release = "";            // QCM release (month year: mmm aaaa).
	var $idqcm = "";              // Id of the QCM (a number).
	var $quest = array();         // Array. Contains all items.
	var $categories = array();    // Array. Contains the different items categories.
	var $nbq = 0;                 // Number of questions.
	var $nbc = 0;                 // Number of categories.
	var $imgfilename = "";        // Name of the graphic file (without extension).
	var $threshold = 0;           // Limit under of the test isn't good.

	// What we want for the test
	var $ponderation = 0;         // 1: Look for correct answers, 0: for points
	var $hmdetails = 2;           // How many details ? 1: global, 2: by category, 3: item after item
	var $typechart = "none";      // Indicate the type of chart.
	var $mailto = "";             // Send mail to this address if not empty.
	var $smsto = "";              // Send SMS to this number if not empty.
	var $charset = "iso-8859-1";  // Define the charset of the content of mails

	// Save properties
	var $media = "none";          // Save in : 'none', 'file', 'mysql', 'postgresql'.
	var $savefile = "";           // Filename in wich save the result (if $media='file').
	var $forfile = "";            // Result of the student for save in a file.
	var $forsql = "";             // Result of the student to be stored in a database.
	var $forsms = "";             // Result of the student to be sent by SMS.
	var $formail = "";            // Result of the student to be sent by mail.

	// SQL properties
	var $host = "localhost";      // Hostname where the database is.
	var $port = "";               // Port of the running database server.
	var $sqlserverset = 0;        // set to 1 if a special sql server is defined
	var $base = "";               // Name of the database.
	var $table = "RTests";        // Name of the table in which save the results.
	var $login = "";              // Login to access at the database server.
	var $password = "";           // Password to access at the database server.

	// What to do when the test is done.
	var $drawchart = 0;           // 1 to draw a chart, 0 for not.
	var $drawsheet = 0;           // 1 to draw a sheet, 0 for not.
	var $save = 0;                // 1 to save result, 0 for not.
	var $sendsms = 0;             // 1 to send an SMS, 0 for not.
	var $sendmail = 0;            // 1 to send a mail, 0 for not.
	var $urlback = "#";           // Eventually, return to that URL when the test is done.

	// Informations about the student
	var $askfor = "none";         // Ask for : 'none', 'name', 'mail', 'phone' or 'name,mail,phone'.
	var $userlist = "none";       // With 'none', the student have to provide his name
	var $userfile = "";           // With 'userlist=file,filename', the combo is made with $userfile
	var $askforname = 0;          // 1 to get student's name, 0 for not.
	var $askformail = 0;          // 1 to get mail address, 0 for not.
	var $askforphone = 0;         // 1 to get phone number, 0 for not.
	var $remoteaddr = "";         // Remote address (IP student).
	var $senderaddr = "";         // Student address (host address or IP address if can't resolve name)
	var $fname = "";              // First name of the student.
	var $lname = "";              // Last name of the student.
	var $patronym = "";           // First and last name of the student
	var $stmail = "";             // Email address of the student.
	var $stsms = "";              // Phone number of the student (for SMS).

	// Statistics
	var $start = 0;               // Start time.
	var $stop = 0;                // Stop time.
	var $date = "";               // Date of the test. (format: "Y-m-d H:i:s").
	var $duration = "";           // Duration of the test.
	var $outtime = 0;             // 0 (intime) ; -1 if duration < mintime ; +1 if duration > maxtime
	var $ca = 0;                  // Correct answers.
	var $ia = 0;                  // Incorrect answers.
	var $na = 0;                  // No answers.
	var $maxpoints = 0;           // Maximum possible points.
	var $score = 0;               // Global number of points obtained.
	var $detail = "";             // Points obtained in each category.
	var $itemsdetails = array();  // Student answers, item after item (if details=large)
	var $capability = 0;          // Ability of the student.


	// ==========================================================================
	// Constructor of this class. Initialize the new object.
	function qcm($idqcm="null") {
		if($idqcm != "null") {
			if($this->is4orSup()) {
				$now = time();
				$this->idqcm = $idqcm;
				$file = "qcm" . $idqcm . ".txt";
				$this->read($file);
				$this->indent = str_repeat("&nbsp;", $this->indentsize);
				// Is it a valid moment for the test ?
				$this->canWeDoIt($now);
				// Student informations
				$this->remoteaddr = $_SERVER["REMOTE_ADDR"];
				$this->senderaddr = gethostbyaddr($this->remoteaddr);
				switch($this->userlist) {
					case "file":
						$user = explode(";",$_POST["user"]);
						$this->fname = ucWords($user[1]);
						$this->lname = strToUpper($user[0]);
						break;
					case "mysql":
						// Later...
						break;
					default:
						$this->fname = ucWords($_POST["fname"]);
						$this->lname = strToUpper($_POST["lname"]);
				}
				$this->patronym = $this->fname . " " . $this->lname;
				if(!$this->askforname) { $this->patronym = $this->t_anon; }
				if(trim($this->patronym) == "") { $this->patronym = $this->t_anon; }
				if($_POST["mail"] && $_POST["mail"] != $this->t_desactivate) {
					$this->sendmail = 1;
					$this->stmail = $_POST["mail"];
				}
				if($_POST["tel"] && $_POST["tel"] != $this->t_desactivate) {
					$this->sendsms = 1;
					$this->stsms = $_POST["tel"];
				}
				$this->imgFilename(); }
			else {
				$this->error(100);
			}
		}
	}


	// --------------------------------------------------------------------------
	// Test of the PHP version
	function is4orSup() {
		$ver = phpversion(); $version = explode(".",$ver);
		if($version[0] >= 4) { return true; } else { return false; }
	}


	// --------------------------------------------------------------------------
	// Can we do the test now ?
	function canWeDoIt($now) {
		if($this->starton && $this->stopon) {
			if(($now-$this->starton < 0) || ($now-$this->stopon > 0)) {
				$this->notNow(1);       // Out of range
			} }
		else {
			if($this->starton) {
				if($now-$this->starton < 0) { $this->notNow(2); } // Before
			}
			if($this->stopon)  {
				if($now-$this->stopon > 0)  { $this->notNow(3); } // After
			}
		}
	}


	// --------------------------------------------------------------------------
	// Send a message if it's not the moment for the test
	function notNow($when) {
		echo "<div class=\"notnow\">";
		switch($when) {
			case 1:
				echo $this->t_between[$this->itq]." &nbsp; ";
				echo date("d/m/Y H:i",$this->starton)." - ";
				echo date("d/m/Y H:i",$this->stopon);
				break;
			case 2:
				echo $this->t_begin[$this->itq]." &nbsp; ".date("d/m/Y H:i",$this->starton);
				break;
			case 3:
				echo $this->t_end[$this->itq]." &nbsp; ".date("d/m/Y H:i",$this->stopon);
				break;
		}
		echo "</div>\n";
		exit;
	}


	// --------------------------------------------------------------------------
	// Calculate the graphic filename ("decimal_IP.timestamp.extension")
	function imgFilename() {
		$addr = $this->remoteaddr;
		$bytes = explode(".",$addr);
		$decaddr = ($bytes[0]*16777216)+($bytes[1]*65536)+($bytes[2]*256)+$bytes[3];
		$moment = date("U");
		$this->imgfilename = $decaddr . "." . $moment . "." . $this->grext;
	}


	// --------------------------------------------------------------------------
	// Read the questionnaire file
	function read($file) {
		$fp = fopen($file,"r");

		// Read the header until a dot alone on a line
		$line = trim(chop(fgets($fp,4096)));
		while($line != ".") {
			if(!$this->isComment($line)) { $header[] = $line; }
			$line = trim(chop(fgets($fp,4096)));
		}
		$this->header($header); unset($header); $this->verifCoherence();

		// Read the items until end of file
		while (!feof ($fp)) {
			$line = trim(chop(fgets($fp,4096)));
			while($line != "") {
				if(!$this->isComment($line)) { $item[] = $line; }
				$line = trim(chop(fgets($fp,4096)));
			}
			$this->quest[] = $item; unset($item);
		}
		$this->nbq = count($this->quest);
	}


	// --------------------------------------------------------------------------
	// Return 1 if the line is a comment (first character is ; or #) or 0 if not.
	function isComment($line) {
		$firstchar = substr($line,0,1);
		if($firstchar == ";" || $firstchar == "#") { return 1; } else { return 0; }
	}


	// --------------------------------------------------------------------------
	// Read the header (QCM properties)
	function header($header) {
		for($n=0; $n<count($header); $n++) {
			$cfg = explode("=",$header[$n]);
			$cfg[0] = trim($cfg[0]); $cfg[1] = trim($cfg[1]);
			$cfg[0] = strtolower($cfg[0]);

			// example: key=value (one value with no space)
			if($cfg[0] == "drawsheet")   { $this->drawsheet = $cfg[1];   }
			if($cfg[0] == "bgcolor")     { $this->bgcolor = $cfg[1];     }
			if($cfg[0] == "indentsize")  { $this->indentisze = $cfg[1];  }
			if($cfg[0] == "imgtype")     { $this->grext = $cfg[1];       }
			if($cfg[0] == "charset")     { $this->charset = $cfg[1];     }
			if($cfg[0] == "title")       { $this->title = $cfg[1];       }
			if($cfg[0] == "showsender")  { $this->showsender = $cfg[1];  }
			if($cfg[0] == "showserver")  { $this->showserver = $cfg[1];  }
			if($cfg[0] == "showtitle")   { $this->showtitle = $cfg[1];   }
			if($cfg[0] == "checkvers")   { $this->checkvers = $cfg[1];   }
			if($cfg[0] == "flagnew")     { $this->flagnew = $cfg[1];     }
			if($cfg[0] == "version")     { $this->version = $cfg[1];     }
			if($cfg[0] == "shownum")     { $this->shownum = $cfg[1];     }
			if($cfg[0] == "release")     { $this->release = $cfg[1];     }
			if($cfg[0] == "goal")        { $this->goal = $cfg[1];        }
			if($cfg[0] == "org")         { $this->org = $cfg[1];         }
			if($cfg[0] == "site")        { $this->site = $cfg[1];        }
			if($cfg[0] == "urlback")     { $this->urlback = $cfg[1];     }
			if($cfg[0] == "ponderation") { $this->ponderation = $cfg[1]; }
			if($cfg[0] == "insertpoint") { $this->insertpoint = $cfg[1]; }
			if($cfg[0] == "threshold")   { $this->threshold = $cfg[1];   }
			if($cfg[0] == "mintime")     { $this->mintime = $cfg[1];     }
			if($cfg[0] == "maxtime")     { $this->maxtime = $cfg[1];     }

			// example: language=en
			if($cfg[0] == "language")    {
				$lgfile = "texts." . $cfg[1] . ".php";
				if(file_exists($lgfile)) { require($lgfile); }
			}

			// example: mailto=hide@address.com
			if($cfg[0] == "mailto")      {
				$this->sendmail = 1;
				$this->mailto = $cfg[1];
			}

			// example: smsto=0683485270
			if($cfg[0] == "smsto")       {
				$this->sendsms = 1;
				$this->smsto = $cfg[1];
			}

			// example: starton=07/05/2004 09:00
			if($cfg[0] == "starton")     { $this->starton = $this->calcTime($cfg[1]); }

			// example: stopon=17/05/2004 09:00
			if($cfg[0] == "stopon")      { $this->stopon = $this->calcTime($cfg[1]);  }

			// example: details=short    ; global
			// example: details=medium   ; by category
			// example: details=large    ; questions and replies
			if($cfg[0] == "details")     {
				// later: if($cfg[1] == "none")  { $this->hmdetails = 0; }				
				if($cfg[1] == "short")  { $this->hmdetails = 1; }
				if($cfg[1] == "medium") { $this->hmdetails = 2; }
				if($cfg[1] == "large")  { $this->hmdetails = 3; }
				if($cfg[1] == "full")   { $this->hmdetails = 4; }
			}

			// example: userlist=none
			// example: userlist=file,usernames.txt
			if($cfg[0] == "userlist")    {
				$users = explode(",",$cfg[1]);
				for($i=0; $i<count($users); $i++) {
					$media[$i] = trim($users[$i]);
				}
				if($users[0] == "file") {
					$this->userlist = "file";
					$this->userfile = $users[1];
				}
			}

			// example: save=none
			// example: save=file,resultats.txt
			// example: save=mysql,TestsOnLine,RTests,pierre,Kz3GU2
			// example: save=postgresql,TestsOnLine,RTests,pierre,Kz3GU2
			if($cfg[0] == "save")        {
				$media = explode(",",$cfg[1]);
				for($i=0; $i<count($media); $i++) {
					$media[$i] = trim($media[$i]);
				}
				if($media[0] == "none") {
					$this->save     = 0;
					$this->media    = "none";
				}
				elseif($media[0] == "file") {
					$this->save     = 1;
					$this->media    = "file";
					$this->savefile = $media[1];
				}
				// If SQL database
				// media=server type,database,table,login,password
				// if the SQL server runs on specific host and port, this
				// can be changed by using sqlServer("host",port) method or
				// the key "sqlserver=host.domain.tld:port"
				elseif($media[0] == "mysql") {
					$this->save     = 1;
					$this->media    = "mysql";
					if(!$this->sqlserverset) {
						$this->host = "localhost";
						$this->port = 3306;
					}
					$this->base     = $media[1];
					$this->table    = $media[2];
					$this->login    = $media[3];
					$this->password = $media[4];
				}
				elseif($media[0] == "postgresql") {
					$this->save     = 1;
					$this->media    = "postgresql";
					if(!$this->sqlserverset) {
						$this->host = "localhost";
						$this->port = 5432;
					}
					$this->base     = $media[1];
					$this->table    = $media[2];
					$this->login    = $media[3];
					$this->password = $media[4];
				}
			}

			// example: sqlserver=sql.domain.fr:3306
			if($cfg[0] == "sqlserver")   {
				$sql = explode(":",$cfg[1]);
				$this->host = trim($sql[0]);
				$this->port = trim($sql[1]);
				$this->sqlserverset = 1;      // SQL server is now defined
			}

			// example: drawchart=none
			// example: drawchart=pie
			if($cfg[0] == "drawchart")   {
				if( ($cfg[1] == "pie")   ||
				    ($cfg[1] == "lines") || ($cfg[1] == "histo") ) {
					$this->drawchart = 1;
					$this->typechart = $cfg[1]; }
				else {
					$this->drawchart = 0;
					$this->typechart = "none";
				}
			}

			// example: author=Pierre FAUQUE,hide@address.com
			if($cfg[0] == "author")      {
				$infoauthor   = explode(",",$cfg[1]);
				$this->author = trim($infoauthor[0]);
				$this->email  = trim($infoauthor[1]);
			}

			// example: askfor=none
			// example: askfor=name
			// example: askfor=mail
			// example: askfor=phone
			// example: askfor=name,mail,phone (indifferent order)
			if($cfg[0] == "askfor")      {
				$this->askforname  = 0;
				$this->askformail  = 0;
				$this->askforphone = 0;
				$this->askfor = trim($cfg[1]);
				$infoask = explode(",",$this->askfor);
				for($m=0; $m<count($infoask); $m++) {
					$infoask[$m] = trim($infoask[$m]);
					if($infoask[$m] == "name")  { $this->askforname  = 1; }
					if($infoask[$m] == "mail")  { $this->askformail  = 1; }
					if($infoask[$m] == "phone") { $this->askforphone = 1; }
				}
			}

			// example: categories=Culture,Réseaux,Protocoles,Logiciels
			if($cfg[0] == "categories")  {
				$cat = explode(",",$cfg[1]);
				$j = 0;
				while($cat[$j]) {
					$this->categories[$j] = array(trim($cat[$j]),0,0,0,0);
					$j++;
				}
				$this->nbc = count($this->categories);
			}

			// example: logo=top,bottom,http://www.fr-webdev.net/img/banner.jpg,center
			if($cfg[0] == "logo") {
				$tab = explode(",",$cfg[1]);
				$pos = 0; $src = -1; $nbe = count($tab);
				for($j=0; $j<$nbe; $j++) {
					$p = strtolower(trim($tab[$j]));
					if($p != "top" && $p != "bottom" && $p != "left" && $p != "center" && $p != "right") {
						$src = $j; }
					else {
						switch($p) {
							case "top"    : $pos += 16; break;
							case "bottom" : $pos +=  8; break;
							case "left"   : $pos +=  4; break;
							case "center" : $pos +=  2; break;
							case "right"  : $pos +=  1; break;
							case "none"   : $pos  =  0; break;
						}
					}
				}
				$this->logopos = $pos;
				if($src >= 0) { $this->logosrc = trim($tab[$src]); }
			}

		}

	}


	// --------------------------------------------------------------------------
	// Return a timestamp from a moment in a "JJ/MM/AAAA HH:MN" format
	function calcTime($moment) {
		$tab = explode(" ",$moment);
		$std = explode("/",$tab[0]);
		$sth = explode(":",$tab[1]);
		// mktime(heure,minute,seconde,mois,jour,an)
		return mktime($sth[0],$sth[1],0,$std[1],$std[0],$std[2]);
	}


	// --------------------------------------------------------------------------
	// Display all items on the screen
	function display() {
		if($this->logopos & 16) { $this->writeLogo(); }
		if($this->showtitle) { $this->writeTitle(); }
		if($this->askfor != "none") { $this->getStudentInfos(); }
		for($n=0; $n<$this->nbq; $n++) {
			if($this->shownum) {
				$nq = $n + 1;
				if($nq < 10) { $nq = "0".$nq.". "; }
				else         { $nq = $nq.". ";     } }
			else {
				$nq = "";
			}
			if($n<10) { $name = "Q0$n"; } else { $name = "Q$n"; }
			$properties = explode(";",$this->quest[$n][0]);
			switch($properties[0]) {
				case 0: $this->writeQCMRU($name,$this->quest[$n],$nq); break;
				case 1: $this->writeQCMRM($name,$this->quest[$n],$nq); break;
				case 2: $this->writeQCMTR($name,$this->quest[$n],$nq); break;
				case 3: $this->writeQCMRE($name,$this->quest[$n],$nq); break;
				case 4: $this->writeQCMSE($name,$this->quest[$n],$nq); break;
				case 5: $this->writeQCMRO($name,$this->quest[$n],$nq); break;
			}
			echo "<hr>\n";
		}
		echo "<input type=\"hidden\" name=\"start\" value=\"" . time() . "\">\n";
		echo "<input type=\"submit\" name=\"submit\" ";
		echo "value=\"$this->t_bt_send\"> &nbsp; \n";
		echo "<input type=\"reset\" value=\"$this->t_bt_clear\"><br>\n";
		$this->writeFoot();
		if($this->logopos & 8) { $this->writeLogo(); }
	}


	// --------------------------------------------------------------------------
	// Write the footpage (author, title, version, release, flag for a new version)
	function writeFoot() {
		echo "<p><div class=\"author\">";
		if($this->author && $this->email) {
			$author = "&copy; <a href=\"mailto:$this->email\">$this->author</a> "; }
		else {
			$author = "";
		}
		$foot = "";
		if($this->title) { $foot .= $this->title; }
		if($this->version) {
			if($foot) { $foot .= " "; }
			$foot .= "v$this->version";
		}
		if($this->release) {
			if($foot) { $foot .= " - "; }
			$foot .= "$this->release";
		}
		if($foot) { $foot = "($foot). "; }
		if($this->checkvers && $this->isObsolete()) {
			$msg = "v$this->newver - $this->refrelease";
			$flag = "<a href=\"javascript:alert('$msg');\" class=\"new\">$this->flagnew</a>";
			$foot .= "<span>$flag</span>";
		}
		echo $author.$foot;
		echo "</p></div>\n";
	}


	// --------------------------------------------------------------------------
	// Display logo or a banner if necessary
	function writeLogo() {
		$align = "";
		if($this->logopos & 4) { $align = "left";   }
		if($this->logopos & 2) { $align = "center"; }
		if($this->logopos & 1) { $align = "right";  }
		if(!$align) { $align = "center"; }
		$image = "<img src=\"$this->logosrc\">";
		echo "<div width=\"100%\" align=\"$align\">$image</div>";
	}


	// --------------------------------------------------------------------------
	// Write the title of the QCM as a title in the web page (in H1 tags)
	function writeTitle() {
		echo "<h1>$this->title<hr></h1>\n";
	}


	// --------------------------------------------------------------------------
	// Draw a form to get student informations: last et first name, phone and mail
	function getStudentInfos() {
		if($this->userlist == "none") {
			$controls = array(
				// array("type","name","size",askfor=1 or not=0)
				array("text","lname","21",$this->askforname),
				array("text","fname","20",$this->askforname),
				array("text","tel","10",$this->askforphone)
         			);
			$text = $this->t_student[0]; }
		else {
			$controls = array(array("text","tel","10",$this->askforphone));
			$text = $this->t_student[1];
		}
		echo "<table border=\"1\">\n";
		$this->tr($text,$controls);
		$controls = array( array("text","mail","64",$this->askformail) );
		$this->tr($this->t_mail,$controls,0);
		echo "</table><hr><p>\n";
	}

	// Write a table row for student information
	function tr($text,$controls,$showlist=1) {
		echo "<tr><td>$text</td><td>";
		if($this->userlist == "file" && $showlist) {
			echo "<select class=\"in\" name=\"user\">\n";
			$hf = fopen($this->userfile,"r");
			while($line=chop(fgets($hf,100))) {
				$user = explode(";",$line);
				echo "<option value=\"$line\">$user[0] $user[1], $user[2]";
			}
			echo "</select>\n";
			fclose($hf);
		}
		for($n=0; $n<count($controls); $n++) {
			if($controls[$n][3]) {
				$mode = ""; $class = "in"; $value = ""; }
			else {
				$mode = " readonly"; $class = "ro"; $value = "$this->t_desactivate";
			}
			echo "<input type=\"".$controls[$n][0]."\" name=\"".$controls[$n][1];
			echo "\" class=\"$class\" value=\"$value\" ";
			echo "size=\"".$controls[$n][2]."\"$mode>";
		}
		echo "</td></tr>\n";
	}


	// --------------------------------------------------------------------------
	// Write an item type 0 (Multiple proposals, unique response)
	function writeQCMRU($name,$item,$nq) {
		echo "<div class=\"q\">".$nq.trim($item[1])."</div>\n";
		for($n=2; $n<count($item); $n++) {
			echo "$this->indent<input type=\"radio\" name=\"$name\" ";
			echo "value=\"".($n-1)."\"> ".trim($item[$n])."<br>\n";
		}
	}


	// --------------------------------------------------------------------------
	// Write an item type 1 (Multiple proposals, multiple responses)
	function writeQCMRM($name,$item,$nq) {
		echo "<div class=\"q\">".$nq.trim($item[1])."</div>\n";
		for($n=2; $n<count($item); $n++) {
			echo "$this->indent<input type=\"checkbox\" name=\"$name"."[]\" ";
			echo "value=\"".($n-1)."\"> ".trim($item[$n])."<br>\n";
		}
	}


	// --------------------------------------------------------------------------
	// Write an item type 2 (Put in order)
	function writeQCMTR($name,$item,$nq) {
		$liste1 = explode(",",$item[2]);
		$liste = "<select name=\"$name"."[]\">"."<option value=\"0\">-- ? --";
		for($n=0; $n<count($liste1); $n++) {
			$liste .= "<option value=\"".($n+1)."\">".trim($liste1[$n]);
		}
		$liste .= "</select>";
		echo "<div class=\"q\">".$nq.trim($item[1])."</div>\n";
		echo "<table border=\"0\">\n";
		for($n=0; $n<count($liste1); $n++) {
			echo "<tr><td>".$this->indent."</td>";
			echo "<td>".($n+1).". </td><td>$liste</td></tr>\n";
		}
		echo "</table>\n";
	}


	// --------------------------------------------------------------------------
	// Write an item type 3 (Make relations)
	function writeQCMRE($name,$item,$nq) {
		$liste1 = explode(",",$item[2]);
		$liste2 = explode(",",$item[3]);
		$liste = "<select name=\"$name"."[]\">"."<option value=\"0\">-- ? --";
		for($n=0; $n<count($liste2); $n++) {
			$liste .= "<option value=\"".($n+1)."\">".trim($liste2[$n]);
		}
		$liste .= "</select>";
		echo "<div class=\"q\">".$nq.trim($item[1])."</div>\n";
		echo "<table border=\"0\">\n";
		for($n=0; $n<count($liste1); $n++) {
			echo "<tr><td>".$this->indent."</td>";
			echo "<td>".trim($liste1[$n])."</td><td>$liste</td></tr>\n";
		}
		echo "</table>\n";
	}


	// --------------------------------------------------------------------------
	// Write an item type 4 (Select an element to complete a proposal)
	function writeQCMSE($name,$item,$nq) {
		$liste1 = explode(",",$item[3]);
		$liste = "<select name=\"$name"."\">"."<option value=\"0\">-- ? --";
		for($n=0; $n<count($liste1); $n++) {
			$liste .= "<option value=\"".($n+1)."\">".trim($liste1[$n]);
		}
		$liste .= "</select>";
		$parts = explode($this->insertpoint,$item[2]);
		$ligne = trim($parts[0])." ".$liste." ".trim($parts[1]);
		echo "<div class=\"q\">".$nq.trim($item[1])."</div>\n";
		echo $this->indent.$ligne;
	}


	// --------------------------------------------------------------------------
	// Write an item type 5 (french: QROC stands for "Question with Short Open Response")
	function writeQCMRO($name,$item,$nq) {
		$parts = explode($this->insertpoint,$item[2]);
		$input = "<input type=\"text\" name=\"$name\" size=\"".$parts[1]."\">";
		$ligne = trim($parts[0])." ".$input." ".trim($parts[2]);
		echo "<div class=\"q\">".$nq.trim($item[1])."</div>\n";
		echo $this->indent.$ligne;
	}


	// --------------------------------------------------------------------------
	// Do the correction of the test and all actions we do (graph, mail, SMS, etc...)
	function correction() {
		$this->correct();                           // Do the correction of the test
		$this->prepareAll();                        // Prepare mails and SMSs
		if($this->drawchart) { $this->graph();    } // Do a graph if asked
		if($this->drawsheet) { $this->sheet();    } // Do a sheet if asked
		if($this->save)      { $this->save();     } // Store the result if asked
		if($this->sendsms)   { $this->sendSMS();  } // Send an SMS if asked
		if($this->sendmail)  { $this->sendMail(); } // Send a mail if asked
		if($this->goal == "opinion") {              // Show a message if it's an inquiry
			echo "<center>$this->t_msgend</center>";
		}
		if($this->urlback != "#") { $this->gotoURL(); }
	}


	// --------------------------------------------------------------------------
	// Write a button to go to an URL when the questionnaire is done.
	function gotoURL() {
		echo "<center><form><input type='button' value=' $this->t_quit '"
		   . " onclick=\"window.location='$this->urlback'\"></form></center>";
	}


	// --------------------------------------------------------------------------
	// Evaluate the student responses
	function correct() {
		global $start;
		$this->stop = time();
		$this->start = $start;
		$this->duration();
		$this->date = date("Y-m-d H:i:s");

		if($this->showtitle) { $this->writeTitle(); }
		for($n=0; $n<$this->nbq; $n++) {
			$this->itemsdetails[] = array($this->quest[$n][1]);
			$properties = explode(";",$this->quest[$n][0]);
			if($properties[0] == 0 || $properties[0] == 4  || $properties[0] == 5) {
				$this->evalUniqueResponse($n,$this->quest[$n]); }
			else {
				$this->evalMultipleResponse($n,$this->quest[$n]);
			}
		}
		if($this->ponderation) {
			$rapport = $this->score / $this->maxpoints;
			$this->capability = ceil(($rapport * 10000) /100); }
		else {
			$this->capability = ceil(($this->ca/$this->nbq * 10000)/100);
		}
		if($this->nbc) {
			$detail = $this->categories[0][0]."="
			        . $this->categories[0][2]."/"
			        . $this->categories[0][1];
			for($n=1; $n<$this->nbc; $n++) {
				$detail .= ","
				        .  $this->categories[$n][0]."="
				        .  $this->categories[$n][2]."/"
					.  $this->categories[$n][1];
			}
			$this->detail = $detail;
		}
	}


	// --------------------------------------------------------------------------
	// Calculate the duration
	function duration() {
		$duration = $this->stop - $this->start; $minute = 60; $hour = 60 * $minute;
		$temps = floor($duration/$hour);
		$temps = "0" . $temps . ":"; $rest = $duration % $hour;
		$mn = floor($rest/$minute);
		if($mn < 10)   { $temps = $temps . "0" . $mn . ":"; }
		else           { $temps = $temps . $mn . ":";       }
		$rest = $rest % $minute;
		if($rest < 10) { $temps = $temps . "0" . $rest;     }
		else           { $temps = $temps . $rest;           }
		$this->duration = $temps;
		//                      mintime              maxtime
		// ------------------------+--------------------+------------------>
		// outtime:      -1                  0                   +1
		if($this->maxtime && $this->mintime) {
			$maxtime = $this->hms2s($this->maxtime);
			$mintime = $this->hms2s($this->mintime);
			$temps   = $this->hms2s($temps);
			if($temps < $mintime) { $this->outtime = -1; }
			if($temps > $maxtime) { $this->outtime =  1; }
		}
	}


	// --------------------------------------------------------------------------
	// Return a number of seconds from a duration in a "HH:MN:SS" format
	function hms2s($hour) {
		$m = explode(":",$hour);
		return ($m[0] * 3600) + ($m[1] * 60) + $m[2];
	}


	// --------------------------------------------------------------------------
	// Verifiy the coherence of header of QCM file and correct some possible errors
	function verifCoherence() {
		// If no category, no graphic possible
		if(!$this->nbc) { $this->typechart = "none"; $this->drawchart = 0; }
		// If only one category : graphic is set to "pie" with no sheetresults.
		if($this->nbc == 1) {
			if($this->drawchart) { $this->typechart = "pie"; }
		}
		// if a userlist have been defined, ask (choose) for the name
		if($this->userlist != "none" && $this->userfile != "") { $this->askforname = 1; }
		// If the questionnaire is an inquiry, some headers may be stupid or not useful
		if($this->goal == "opinion") {
			$this->typechart   = "none";
			$this->drawchart   = 0;
			$this->drawsheet   = 0;
			$this->ponderation = 0;
			$this->threshold   = 0;
			$this->askforphone = 0;
			$this->sendsms     = 0;
			$this->smsto       = "";
			$this->hmdetails   = 3;   // Full details
			$this->itq         = 1;   // It's not a test but an inquiry
		}
		// Correction of graphic extension uncorrectly wrote
		if($this->grext == "jpg") { $this->grext = "jpeg"; }
		// If no logo have been defined, unset its position
		if(!$this->logosrc) { $this->logopos = 0; }
		// Unset the position logo if several alignments have been defined
		if(($this->logopos & 4) && ($this->logopos & 2)) { $this->logopos = 0; }
		if(($this->logopos & 4) && ($this->logopos & 1)) { $this->logopos = 0; }
		if(($this->logopos & 2) && ($this->logopos & 1)) { $this->logopos = 0; }
	}

					
	// --------------------------------------------------------------------------
	// Evaluate the response of a QCMRU (types 0, 4 or 5)
	function evalUniqueResponse($n,$item) {
		if($n<10) { $name = "Q0$n"; } else { $name = "Q$n"; }
		$properties = explode(";",$item[0]);
		$type = $properties[0];
		$category = $properties[1]-1;
		if($type == 5) {
			$reponse = strtolower($_POST["$name"]);
 			$correctresponse = strtolower($properties[2]);}
		else {
			$reponse = $_POST["$name"];
			$correctresponse = $properties[2];
		}
		$points = $properties[3];
		$this->maxpoints += $points;
		if(!$reponse) { $this->itemsdetails[$n][] = $this->t_noresponse; }
		else {
			switch($type) {
				case 4:
					$list = explode(",", $item[3]);
					$this->itemsdetails[$n][] = $list[$reponse-1];
					break;
				case 5:
					$this->itemsdetails[$n][] = $_POST["$name"];
					break;
				default:
					$this->itemsdetails[$n][] = $this->quest[$n][$reponse+1];
					break;
			}
		}
		if($this->nbc) {
			$this->categories[$category][1]++;
			$this->categories[$category][3] += $points;
		}
		if(trim($reponse) == trim($correctresponse)) {
			if($this->nbc) {
				$this->categories[$category][2]++;
				$this->categories[$category][4] += $points;
			}
			$this->ca++;
			$this->score += $points; }
		else {
			if(!$reponse) {
				$this->na++; }
			else {
				$this->ia++;
			}
		}
	}


	// --------------------------------------------------------------------------
	// Evaluate the response of an item with multiple responses (types 1, 2 or 3)
	function evalMultipleResponse($n,$item) {
		if($n<10) { $name = "Q0$n"; } else { $name = "Q$n"; }
		$tabrep = $_POST["$name"];
		$properties = explode(";",$item[0]);
		$type = $properties[0];
		$category = $properties[1]-1;
		$correctresponse = $properties[2];
		$points = $properties[3];
		$this->maxpoints += $points;
		switch($type) {
			case 1: for($i=0; $i<count($tabrep); $i++) {
					$this->itemsdetails[$n][] = $this->quest[$n][$tabrep[$i]+1];
				}
				if(count($tabrep) == 0) { $this->itemsdetails[$n][] = $this->t_noresponse; }
				break;
			case 2: $liste = explode(",",$this->quest[$n][2]);
				for($i=0; $i<count($tabrep); $i++) {
					$this->itemsdetails[$n][] = ($i+1).". ".$liste[$tabrep[$i]-1];
				}
				break;
			case 3: $liste = explode(",",$this->quest[$n][2]);
				$proposals = explode(",",$this->quest[$n][3]);
				for($i=0; $i<count($tabrep); $i++) {
					$this->itemsdetails[$n][] = $liste[$i]." => ".$proposals[$tabrep[$i]-1];
				}
				break;
		}
		if($this->nbc) {
			$this->categories[$category][1]++;
			$this->categories[$category][3] += $points;
		}
		$reponse = $tabrep[0]; $norep = "0";
		for($n=1;$n<count($tabrep); $n++) {
			$reponse .= "," . $tabrep[$n];
			$norep .= ",0";
		}
		if($reponse == trim($correctresponse)) {
			if($this->nbc) {
				$this->categories[$category][2]++;
				$this->categories[$category][4] += $points;
			}
			$this->ca++;
			$this->score += $points; }
		else {
			if(!$reponse || $reponse == $norep) {
				$this->na++; }
			else {
				$this->ia++;
			}
		}
	}


	// --------------------------------------------------------------------------
	// Prepare save records (file or database), mail and SMS if asked
	function prepareAll() {

		// Prepare for save in a file
		if($this->media == "file") {
			$this->prepareFile();
		}

		// Prepare for save in a database
		if($this->media == "mysql" || $this->media == "postgresql") {
			$this->prepareSQL();
		}

		// Prepare the mail to send
		if($this->sendmail) {
			$this->prepareMail();
		}

		// Prepare the sms to send
		if($this->sendsms) {
			$this->prepareSMS();
		}

	}


	// --------------------------------------------------------------------------
	// Write all questions and the given responses ($sep = separator: \n|<br>)
	function reportAllItems($sep) {
		$allresp = $this->t_yourreplies.$sep.$sep;
		for($n=0; $n<count($this->itemsdetails); $n++) {
			for($m=0; $m<count($this->itemsdetails[$n]); $m++) {
				$allresp .= $this->itemsdetails[$n][$m] . $sep;
			}
			$allresp .= $sep;
		}
		return $allresp;
	}


	// --------------------------------------------------------------------------
	// Write all questions and the good responses ($sep = separator: \n|<br>)
	function reportGoodResponses($dest="web") {
		if($dest == "web") { $arrow = "=&gt;"; $sep = "<br/>"; } else { $arrow = "=>"; $sep = "\n"; }
		$responses = $this->t_correction.$sep.$sep;
		for($n=0; $n<count($this->quest); $n++) {
			$properties = explode(";",$this->quest[$n][0]); $goodrep = $properties[2];
			$responses .= ($n+1) . ". " . $this->quest[$n][1].$sep;
			switch($properties[0]) {
				case 0:               // QCMRU
					for($i=2; $i<count($this->quest[$n]); $i++) {
						if($i == $goodrep+1) { $responses .= $this->quest[$n][$i].$sep; }
					}
					break;
				case 1:               // QCMRM
					$tabrep = explode(",",$goodrep); $nbrep = count($tabrep);
					for($i=2; $i<count($this->quest[$n]); $i++) {
						for($j=0; $j<$nbrep; $j++) {
							if($tabrep[$j]+1 == $i) { $responses .= $this->quest[$n][$i].$sep; }
						}
					}
					break;
				case 2:               // Sort
					$list = explode(",",$this->quest[$n][2]);
					$tabrep = explode(",",$goodrep); $nbrep = count($tabrep);
					for($i=0; $i<$nbrep; $i++) {
						$responses .= ($i+1).". ".$list[$tabrep[$i]-1].$sep;
					}
					break;
				case 3:               // Associations
					$list1 = explode(",",$this->quest[$n][2]); $nbl1 = count($list1);
					$list2 = explode(",",$this->quest[$n][3]);
					$tabrep = explode(",",$goodrep); $nbrep = count($tabrep);
					for($i=0; $i<$nbl1; $i++) {
						$responses .= $list1[$i]." ".$arrow." ";
						$responses .= $list2[$tabrep[$i]-1].$sep;
					}
					break;
				case 4:
					$tabline = explode("$this->insertpoint",$this->quest[$n][2]);
					$tablist = explode(",",$this->quest[$n][3]);
					$responses .= $tabline[0]."\"".$tablist[$goodrep-1]."\"".$tabline[1].$sep;
					break; // Completion
				case 5:               // QROC
					$tabline = explode("$this->insertpoint",$this->quest[$n][2]);
					$responses .= $tabline[0]."\"$goodrep\"".$tabline[2].$sep;
					break;
			}
			$responses .= $sep;
		}
		return $responses;
	}
	function showGoodResponses() { echo $this->reportGoodResponses(); }


	// --------------------------------------------------------------------------
	// Prepare the line to be write in the save file if file is asked
	function prepareFile() {
		if($this->capability >= $this->threshold) { $sanction = $this->t_ok; }
		else                                      { $sanction = $this->t_ko; }
		$file = "$this->date;$this->lname;$this->fname;$this->stmail;"
		      . "$this->stsms;$this->remoteaddr;$this->idqcm;"
		      . "$this->version;$this->duration;NBQ=$this->nbq;"
		      . "BR=$this->ca;MR=$this->ia;NR=$this->na;$this->capability%;"
		      . "$this->detail;$sanction\n";
		$this->forfile = $file;
	}


	// --------------------------------------------------------------------------
	// Prepare the needed SQL request to store the result in the database.
	function prepareSQL() {
		// Give a value to $statfields and $statvals according to the key details
		$suppfields = ""; $suppvalues = ""; $statfields = ""; $statvals = "";
		switch($this->hmdetails) {
			case 2:
				$suppfields = ",detail";
				$suppvalues = ",'$this->detail'";
				break;
			case 3:
				$fdc = addslashes($this->reportAllItems("\\n"));
				$suppfields = ",detail,fdetails";
				$suppvalues = ",'$this->detail','$fdc'";
				break;
		}
		$fields = "(date,lname,fname,email,tel,raddr,idqcm,version,"
		        . "duration,nbq,nbca,nbia,nbna,skills".$suppfields.")";
		$values = "('$this->date','$this->lname','$this->fname','$this->stmail',"
		        . "'$this->stsms','$this->remoteaddr','$this->idqcm',"
		        . "'$this->version','$this->duration','$this->nbq',"
		        . "'$this->ca','$this->ia','$this->na','$this->capability'"
		        . $suppvalues . ");";
		$this->forsql = "insert into $this->table $fields values $values";
	}


	// --------------------------------------------------------------------------
	// Prepare the mail body
	function prepareMail() {
		$msg  = $this->t_subject[$this->itq]." : $this->title\n$sender"
		      . $this->t_testdate[$this->itq]." : $this->date\n"
		      . $this->t_playtime[$this->itq]." : $this->duration\n";
		$head = $msg;
		if($this->ponderation) {
			if($this->score > 1) { $t_p = $this->t_point[1]; }
			else                 { $t_p = $this->t_point[0]; }
			$msg .= "$this->t_nbpoints : $this->score $t_p "
			     .  "$this->t_on $this->maxpoints\n";
		}
		$msg .= "$this->t_nbquestions : $this->nbq\n"
		      . "$this->t_nbcresponses : $this->ca\n"
		      . "$this->t_nbiresponses : $this->ia\n"
		      . "$this->t_nbnresponses : $this->na\n"
		      . "$this->t_competence : $this->capability%\n\n";
		if($this->nbc && ($this->hmdetails > 1)) {
			for($n=0; $n<$this->nbc; $n++) {
				if($this->categories[$n][2] > 1) {
					$t_r = $this->t_response[1]; }
				else {
					$t_r = $this->t_response[0];
				}
				if($this->categories[$n][4] > 1) {
					$t_p = $this->t_point[1]; }
				else {
					$t_p = $this->t_point[0];
				}
				$msg .= $this->categories[$n][0] . " : "
				     .  $this->categories[$n][2] . " $t_r $this->t_on "
				     .  $this->categories[$n][1];
				if($this->ponderation) {
					$msg .= " (" . $this->categories[$n][4] . " $t_p)";
				}
				$msg .= "\n";
			}
		}
		if($this->hmdetails >= 3) { $msg .= "\n\n" . $this->reportAllItems("\n"); }
		if($this->goal == "opinion") { $msg = "$head\n" . $this->reportAllItems("\n"); }
		$this->formail = $msg;
		$this->mailsign .= "\n---\n$this->org\n"
		                .  $this->t_subject[$this->itq]." : $this->site";

	}


	// --------------------------------------------------------------------------
	// Prepare the SMS text to be sent if an SMS is asked
	function prepareSMS() {
		$note = floor(($this->capability/5)*100)/100;
		$sms  = "$this->patronym. $this->t_smsduracy: $this->duration. "
		      . "$this->t_smsnbq: $this->nbq, "
		      . "$this->t_smsca : $this->ca, "
		      . "$this->t_smsia : $this->ia. "
		      . "$this->t_smsnote: $note/20";
		$this->forsms = $sms;
	}


	// --------------------------------------------------------------------------
	// Draw the asked graphic
	function graph() {
		if($this->drawchart) {
			if ($this->typechart == "lines") { $this->draw_lines(); }
			if ($this->typechart == "histo") { $this->draw_histo(); }
			if ($this->typechart == "pie")   { $this->draw_pie();   }
		}
	}


	// --------------------------------------------------------------------------
	// Draw a lines graph
	function draw_lines() {
		$date = date("j/m/Y H:i",time());
		$titre = "$this->patronym. $date. ".$this->title;
		$pts = 0; $tg = 0;
		for($n=0; $n<$this->nbc; $n++) {
			$libel  = $this->categories[$n][0];
			$maxth  = $this->categories[$n][1]; $tg  += $this->categories[$n][1];
			$total  = $this->categories[$n][2]; $pts += $this->categories[$n][2];
			$pc     = floor($total/$maxth * 10000)/100;
			$tb[$n] = $pc * 2;
			$tl[$n] = $this->categories[$n][0];
		}
		// $this->capability = ceil(($pts/$tg * 10000)/100);

		$nbe = Sizeof($tb); $total = 0; $max = 0;
		$max = 200; $largeur = ($nbe*50) + 250; $hauteur = $max + 100;

		$ox1 = 50;   $oy1 = 50;	  $ox2 = $ox1; $oy2 = $oy1 + $max;
		$ax1 = $ox1; $ay1 = $oy2; $ax2 = $ax1 + ($nbe*50); $ay2 = $oy2;

		$img = ImageCreate($largeur,$hauteur);

		$cyan   = ImageColorAllocate($img,210,250,244);
		$black  = ImageColorAllocate($img,0,0,0);
		$blue   = ImageColorAllocate($img,0,0,200);
		$grey   = ImageColorAllocate($img,200,200,200);
		$red    = ImageColorAllocate($img,255,0,0);

		ImageFill($img,0,0,$cyan);

		// Drawing the level lines
		$n = 20;
		for ($y=$max+30; $y>50; $y=$y-20) {
			ImageLine($img,$ax1,$y,$ax2,$y,$grey);
			$lg = $n/2;
			if ($lg<100) { $lg = " " . $lg; }
			$lg = $lg . "%";
			ImageString($img,2,17,$y-7,$lg,$blue);
			$n = $n + 20;
		}

		// Drawing axis
		ImageLine($img,$ox1,$oy1,$ox2,$oy2,$black);
		Imageline($img,$ax1,$ay1,$ax2,$ay2,$black);

		// Write the X-axis legend
		$depart = 71;
		for ($i=0; $i<$nbe; $i++) {
			if ($i<10) { $l = "0" . $i; } else { $l = $i; }
			ImageString($img,2,$depart,$hauteur-45,$l,$blue);
			$depart = $depart + 50;
		}

		$origx = 75;
		$origy = 50 + ($max-$tb[0]);

		// Draw the curve
		for ($i=0; $i<$nbe-1; $i++) {
			$x1 = $origx;      $y1 = $origy;
			$x2 = $origx + 50; $y2 = 50 + ($max-$tb[$i+1]);
			ImageLine($img,$x1,$y1,$x2,$y2,$red);
			$origx = $x2; $origy = $y2;
		}

		// Write textual legends
		for ($i=0; $i<$nbe; $i++) {
			$lgd = $i;
			if ($i<10) { $lgd = "0" . $lgd; }
			$lgd = $lgd . ". " . $tl[$i];
			ImageString($img,2,$ax2+20,20+($i*15),$lgd,$blue);
		}
		ImageString($img,5,50,20,$this->t_yourresults,$blue);
		ImageString($img,2,10,$hauteur-20,$titre,$blue);
		switch($this->grext) {
			case "gif":  ImageGIF($img,$this->imgfilename);  break;
			case "jpeg": ImageJPEG($img,$this->imgfilename); break;
			case "png":  ImagePNG($img,$this->imgfilename);  break;
		}
		echo "<center><img src=\"$this->imgfilename\" border=\"1\"></center>\n";
	}


	// --------------------------------------------------------------------------
	// Draw an histogram graph
	function draw_histo() {
		$date= date("j/m/Y H:i",time());
		$titre = "$this->patronym. $date. ".$this->title;
		$pts = 0; $tg = 0;
		for($n=0; $n<$this->nbc; $n++) {
			$libel = $this->categories[$n][0];
			$maxth = $this->categories[$n][1]; $tg  += $this->categories[$n][1];
			$total = $this->categories[$n][2]; $pts += $this->categories[$n][2];
			$pc = floor($total/$maxth * 10000)/100;
			$tb[$n] = $pc * 2;
			$tl[$n] = $this->categories[$n][0];
		}

		$nbe = Sizeof($tb); $total = 0; $max = 0;
		$max = 200; $largeur = ($nbe*50) + 250; $hauteur = $max + 100;

		$ox1 = 50;   $oy1 = 50;	  $ox2 = $ox1; $oy2 = $oy1 + $max;
		$ax1 = $ox1; $ay1 = $oy2; $ax2 = $ax1 + ($nbe*50); $ay2 = $oy2;

		$img = ImageCreate($largeur,$hauteur);

		$cyan   = ImageColorAllocate($img,210,250,244);
		$black  = ImageColorAllocate($img,0,0,0);
		$blue   = ImageColorAllocate($img,0,0,200);
		$grey   = ImageColorAllocate($img,200,200,200);
		$green  = ImageColorAllocate($img,0,150,0);

		ImageFill($img,0,0,$cyan);

		// Drawing the level lines
		$n = 20;
		for ($y=$max+30; $y>50; $y=$y-20) {
			ImageLine($img,$ax1,$y,$ax2,$y,$grey);
			$lg = $n/2;
			if ($lg<100) { $lg = " " . $lg; }
			$lg = $lg . "%";
			ImageString($img,2,17,$y-7,$lg,$blue);
			$n = $n + 20;
		}

		// Drawing axis
		ImageLine($img,$ox1,$oy1,$ox2,$oy2,$black);
		Imageline($img,$ax1,$ay1,$ax2,$ay2,$black);

		// Write the X-axis legend
		$depart = 71;
		for ($i=0; $i<$nbe; $i++) {
			if ($i<10) { $l = "0" . $i; } else { $l = $i; }
			ImageString($img,2,$depart,$hauteur-45,$l,$blue);
			$depart = $depart + 50;
		}

		$origx = 75;
		$origy = 50 + ($max-$tb[0]);

		// Draw the curve
		for ($i=0; $i<$nbe; $i++) {
			$x1 = $origx; $y2 = 50 + ($max-$tb[$i]);
			ImageFilledRectangle($img,$x1-15,$y2,$x1+15,$hauteur-50,$green);
			$origx = $x1 + 50;
		}

		// Write textual legends
		for ($i=0; $i<$nbe; $i++) {
			$lgd = $i;
			if ($i<10) { $lgd = "0" . $lgd; }
			$lgd = $lgd . ". " . $tl[$i];
			ImageString($img,2,$ax2+20,20+($i*15),$lgd,$blue);
		}
		ImageString($img,5,50,20,$this->t_yourresults,$blue);
		ImageString($img,2,10,$hauteur-20,$titre,$blue);
		switch($this->grext) {
			case "gif":  ImageGIF($img,$this->imgfilename);  break;
			case "jpeg": ImageJPEG($img,$this->imgfilename); break;
			case "png":  ImagePNG($img,$this->imgfilename);  break;
		}
		echo "<center><img src=\"$this->imgfilename\" border=\"1\"></center>\n";
	}


	// --------------------------------------------------------------------------
	// Draw a pie graph
	function draw_pie() {
		$result[0] = $this->capability;
		$result[1] = 100 - $result[0];
		$this->pie_chart($result);
		echo "<center><img src=\"$this->imgfilename\" border=\"1\"></center>\n";
	}

	// Create the chart, sector by sector and build the legend.
	function pie_chart($tab) {
		$largeur = 450; $hauteur = 300;
		$date= date("j/m/Y H:i",time());
		$titre = "$this->patronym. $date. ".$this->title;
		$img = ImageCreate($largeur,$hauteur);

		$black    = ImageColorAllocate($img,0,0,0);
		$blue     = ImageColorAllocate($img,0,0,200);
		$color[0] = ImageColorAllocate($img,128,255,255);
		$color[1] = ImageColorAllocate($img,255,255,255);

		ImageFill($img,0,0,$color[1]);
		$total = 0;
		for ($i=0; $i<SizeOf($tab); $i++) {
			$total = $total + $tab[$i];
		}
		if ($total == 0) {
			ImageString($img,3,90,140,$this->t_nopoints,$black);
			switch($this->grext) {
				case "gif":  ImageGIF($img,$this->imgfilename);  break;
				case "jpeg": ImageJPEG($img,$this->imgfilename); break;
				case "png":  ImagePNG($img,$this->imgfilename);  break;
			}
			return;
		}
		$unite = 360/$total; $begin = 0;
		for ($i=0; $i<SizeOf($tab); $i++) {
			$part = $tab[$i]*$unite;
			$this->portion($part,$begin,$color[$i],$img,$tab[$i],$i);
			$begin = $begin + $part;
		}
		$x1 = 300; $x2 = $x1+20; $y = 35;
		ImageFilledRectangle($img,$x1,$y,$x2,$y+20,$color[0]);
		ImageString($img,5,$x1+30,$y+3,$this->t_youknow,$blue); $y=25;
		for ($i=0; $i<SizeOf($this->categories); $i++) {
			srand((double)microtime()*1000000);
			$coul = ImageColorAllocate($img,rand(0,180),rand(0,180),rand(0,180));
			$y1 = $y + 35 + (25*$i); $y2 = $y1+20;
			$max = $this->categories[$i][1];
			$points = $this->categories[$i][2];
			$pc = ceil($points*10000/$max)/100;
			$text = $this->categories[$i][0] . " (" . $pc . "%)";
			ImageString($img,2,$x1+30,$y2-16,$text,$black);
		}
		ImageString($img,2,10,$hauteur-20,$titre,$blue);
		switch($this->grext) {
			case "gif":  ImageGIF($img,$this->imgfilename);  break;
			case "jpeg": ImageJPEG($img,$this->imgfilename); break;
			case "png":  ImagePNG($img,$this->imgfilename);  break;
		}
	}

	// Draw a sector
	function portion($epaisseur,$dep,$couleur,$image,$caption,$num) {
		$black  = ImageColorAllocate($image,0,0,0);
		for ($i=250;$i>=0; $i--) {
			ImageArc($image,150,150,$i,$i,$dep,$dep+$epaisseur,$couleur);
		}
		$alpha = ($dep + ($epaisseur/2)) * ((2*M_PI)/360);
		$cs = 140 * cos($alpha);
		$sn = 140 * sin($alpha);
		$xt = 150 + $cs;
		$yt = 150 + $sn;
	}


	// --------------------------------------------------------------------------
	// Write the results in a sheet
	function sheet() {
		echo "<p><center>\n<table border=\"1\" width=\"60%\">\n";
		echo "<tr bgcolor=\"#000090\"><td align=\"center\">";
		echo "<font color=\"FFFFFF\">$this->t_sections</font></td>";
		echo "<td align=\"center\"><font color=\"FFFFFF\">";
		echo "$this->t_statistics</font></td></tr>\n";
		if($this->ponderation) {
			echo "<tr><td>&nbsp;$this->t_nbpoints&nbsp;</td>";
			echo "<td align=\"right\">$this->score $this->t_on ";
			echo "$this->maxpoints&nbsp;</td></tr>\n";
		}
		echo "<tr><td>&nbsp;$this->t_nbquestions&nbsp;</td>";
		echo "<td align=\"right\">$this->nbq&nbsp;</td></tr>\n";
		echo "<tr><td>&nbsp;$this->t_nbcresponses&nbsp;</td>";
		echo "<td align=\"right\">$this->ca&nbsp;</td></tr>\n";
		echo "<tr><td>&nbsp;$this->t_nbiresponses&nbsp;</td>";
		echo "<td align=\"right\">$this->ia&nbsp;</td></tr>\n";
		echo "<tr><td>&nbsp;$this->t_nbnresponses&nbsp;</td>";
		echo "<td align=\"right\">$this->na&nbsp;</td></tr>\n";
		echo "<tr><td>&nbsp;".$this->t_playtime[$this->itq]."&nbsp;</td>";
		echo "<td align=\"right\">$this->duration&nbsp;</td></tr>\n";
		echo "<tr bgcolor=\"#FFFF80\"><td>&nbsp;$this->t_competence&nbsp;</td>";
		echo "<td align=\"right\">$this->capability %&nbsp;</td></tr>\n";
		$n=0;
		if($this->nbc > 1) {
			while($this->categories[$n][0]) {
				if($this->categories[$n][2] > 1) { $t_r = $this->t_response[1]; }
				else                             { $t_r = $this->t_response[0]; }
				echo "<tr><td align=\"right\">";
				echo $this->categories[$n][0] . "&nbsp;&nbsp;&nbsp;</td>";
				echo "<td align=\"right\">";
				echo $this->categories[$n][2] . " $t_r $this->t_on ";
				echo $this->categories[$n][1];
				if($this->ponderation) {
					if($this->categories[$n][4] > 1) { $t_p = $this->t_point[1]; }
					else                             { $t_p = $this->t_point[0]; }
					echo " (" . $this->categories[$n][4] . " $t_p)";
				}
				echo "&nbsp;</td></tr>\n";
				$n++;
			}
		}
		echo "</table>\n</center><p>\n";
	}


	// --------------------------------------------------------------------------
	// Save the result either in a text file or in a database
	function save() {
		if($this->save == "1") {
			if($this->media == "file") {
				$fp = fopen($this->savefile,"a");
				$r = fputs($fp,$this->forfile);
				fclose($fp);
			}
			$host = $this->host;  $port = $this->port; $base = $this->base;
			$user = $this->login; $pass = $this->password;
			if($this->media == "mysql") {
				if(!@$cnx = mysql_pconnect("$host:$port",$user,$pass)) {
					$this->error(200);
				}
				if(!@$ok = mysql_select_db($base, $cnx)) {
					$this->error(201);
				}
				$r = $this->execRequete($this->forsql,$cnx);
			}
			if($this->media == "postgresql") {
				$param = "host=$host port=$port user=$user "
				       . "password=$pass dbname=$base";
				if(!@$cnx = pg_pconnect($param)) {
					$this->error(203);
				}
				$r = $this->execRequete($this->forsql,$cnx);
			}
		}
	}

	// Execute a request on the database
	function execRequete($requete,$connexion) {
		if($this->media == "mysql") {
			$resultat = mysql_query($requete, $connexion);
		}
		if($this->media == "postgresql") {
			$resultat = pg_query($connexion, $requete);
		}
		if ($resultat) { return $resultat;  }
		else           { $this->error(202); }
	}


	// ##########################################################################
	// --------------------------------------------------------------------------
	// Send an SMS from my professional intranet
	function sendSMS() {
		//
		// This method is empty in this distribued version for internet.
		// It is used on my professional intranet and is specific to it.
		// You have to write this method if you want this feature for internet.
		// If you write it, the body of the SMS is $this->forsms
		//
	}
	// --------------------------------------------------------------------------
	// ##########################################################################


	// --------------------------------------------------------------------------
	// Send the result by mail
	function sendMail() {
		$subject = $this->t_subject[$this->itq].": $this->patronym.";
		if($this->showsender && ($this->remoteaddr != $this->senderaddr)) {
			 $sender = "From-Host : $this->senderaddr\n"; }
		else { $sender = "\n"; }
		if($this->showserver) {
			$ipserver = $_SERVER["SERVER_ADDR"];
			$sender .= "QCM-Server: " . $_SERVER["SERVER_NAME"] . " ($ipserver)\n";
		}
		$headers .= "From: $this->email\n"
		         .  "Reply-to: $this->email\n"
		         .  "QCM-Class: version $this->qcmver\n"
		         .  "From-IP: $this->remoteaddr\n$sender"
		         .  "X-Mailer: Script PHP/".phpversion()."\n"
		         .  "MIME-Version: 1.0\n"
			   .  "Content-type: text/plain; charset=$this->charset\n";
		$msgnew = "";
		if($this->checkvers && $this->isObsolete()) {
			$msgnew = "\n$this->t_newvers:\n$this->qcmdnl\n";
		}
		if($this->hmdetails == 4) { $goodresp = "\n".$this->reportGoodResponses("mail"); }
		if($this->stmail) {
			mail($this->stmail, $subject, $this->formail.$goodresp.$this->mailsign, $headers);
		}
		if($this->mailto) {
			mail($this->mailto, $subject, $this->formail.$this->mailsign.$msgnew, $headers);
		}
	}


	// ==========================================================================
	// Method if SQL server runs with non standard port
	// Use this method before the correction() method.
	// or use the key "sqlserver=host.domain.tld:port"
	function sqlServer($server,$port) {
		$this->host = $server;   // hostname or IP number
		$this->port = $port;     // database port server
	}


	// --------------------------------------------------------------------------
	// Send an error page according to the error type.
	function error($error) {
		$txterror = $this->error[$error];
		echo "<html>\n<head>\n<title>Error $error</title>\n<head>\n";
		echo "<body bgcolor=\"$this->bgcolor\">\n&nbsp;";
		echo "<h1><i><font color=\"#000090\">$this->t_qcmerror</i> : </font>";
		echo "<font color=\"#FF0000\">$error</font></h1>\n";
		echo "<blockquote>\n<h2>$txterror</h2>\n<pre>$this->forsql</pre></blockquote>\n";
		echo "</body>\n</html>\n";
		exit;
	}


	//  #################################################################
	//  #                Extra information functions                    #
	//  #################################################################

	// --------------------------------------------------------------------------
	// Define a message that will be displayed after an inquiry
	function msgEnd($msg) {
		$this->t_msgend = $msg;
	}

	// --------------------------------------------------------------------------
	// Return 1 if a new version exists
	function isObsolete() {
		$hf = fopen("$this->qcmurl/lastver.qcm.txt","r");
		$line = chop(fgets($hf,4096)); fclose($hf); $parts = explode(";",$line);
		$this->newver = $parts[0]; $this->refrelease = $parts[1];
		$nver = explode(".",$parts[0]); $this->refver = ($nver[0]*1000) + $nver[1];
		if($this->refver > $this->curver) { return 1; } else { return 0; }
	}

	// --------------------------------------------------------------------------
	// Verify the version of this class
	function about() {
		echo "<pre><hr>Class QCM v-$this->qcmver ($this->qcmrelease)\n";
		echo "&copy $this->qcmauthor <a href=\"mailto:$this->qcmemail\">$this->qcmemail</a>\n";
		$hf = fopen($this->qcmurl."/lastver.qcm.txt","r");
		$line = chop(fgets($hf,4096));
		$infos = explode(";",$line);
		if($this->qcmver == $infos[0]) {
			echo "You're using the lastest version.\n"; }
		else {
			$ver = explode(".",$infos[0]);
			$dnlurl = $this->qcmurl."/cqcm".$ver[0].$ver[1].".zip";
			echo "\nYou're using the version $this->qcmver ($this->qcmrelease) of this class.\n";
			echo "There is a version ".$infos[0]." (".$infos[1].") on the original server.\n";
			echo "Would you like to <a href=\"$dnlurl\">download that version</a> ?\n";
			echo "Features: ".$infos[2]."\n";
		}
		echo "<hr></pre>\n";
	}

	// --------------------------------------------------------------------------
	// Go to the correct URL to report a bug found in this class
	function reportBug() {
		echo "<pre><hr>Class QCM version $this->qcmver : ";
		echo "<a href=\"$this->qcmbug\">$this->t_reportbug</a><hr></pre>";
	}

	// --------------------------------------------------------------------------
	// Go to the correct URL to ask for a new feature
	function askWish() {
		echo "<pre><hr>Class QCM version $this->qcmver : ";
		echo "<a href=\"$this->qcmwish\">$this->t_wish</a><hr></pre>";
	}

	// --------------------------------------------------------------------------
	// Verify the version of this class
	function copyright() {
		$msg  = "<pre><hr>Class QCM version $this->qcmver, a questionnaire manager<br>"
		      . "&copy; $this->qcmrelease $this->qcmauthor, "
		      . "<a href=\"mailto:$this->qcmemail\">$this->qcmemail</a>.\n\n"
		      . "This program is free software; you can redistribute it and/or modify it under\n"
		      . "the terms of the Aladdin Free Public License version 9, September 18, 2000\n"
		      . "published by Aladdin Enterprises: Menlo Park, California, U.S.A.\n\n"
		      . "NOTE: This license is not the same as any of the GNU Licenses\n"
		      . "published by the Free Software Foundation. Its terms are\n"
		      . "substantially different from those of the GNU Licenses.\n"
		      . "If you are familiar with the GNU Licenses, please read this\n"
		      . "Aladdin Free Public License, provided with this class, with extra care.\n"
		      . "THE PROGRAM IS PROVIDED TO YOU \"AS IS\" WITHOUT WARRANTY.<hr></pre>\n";
		echo $msg;
	}

	// --------------------------------------------------------------------------
	// Display the requested information : about|bug|wish|copyright
	function info($what) {
		echo "<style type=\"text/css\">a {text-decoration:none}</style>\n";
		if($what == "about")     { $this->about();     }
		if($what == "bug")       { $this->reportBug(); }
		if($what == "wish")      { $this->askWish();   }
		if($what == "copyright") { $this->copyright(); }
	}

	//  #################################################################
	//  #                   Debugging functions                         #
	//  #################################################################


	// --------------------------------------------------------------------------
	// Show the correction
	function showCorrection() {
		// Remove after. Just for test.
		echo "<hr size=\"4\" noshade>";
		echo "Nombre total de questions: ".$this->nbq."<br>\n";
		echo "Nombre maximum de points: ".$this->maxpoints."<br>\n";
		echo "Nombre de bonnes réponses: ".$this->ca."<br>\n";
		echo "Nombre de mauvaises réponses: ".$this->ia."<br>\n";
		echo "Nombre de non réponses: ".$this->na."<br>\n";
		echo "Nombre de points acquis: ".$this->score."<br>\n";
		for($n=0; $n<count($this->categories); $n++) {
			echo "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
			echo "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
			echo $this->categories[$n][0].": ";
			echo $this->categories[$n][2]." (";
			echo $this->categories[$n][3]." points)<br>\n";
		}
		echo "<hr size=\"4\" noshade>";
	}


	// --------------------------------------------------------------------------
	// Display the used variables in a table
	function showVars() {
		$n = 0;
		$V[$n][0] = "\$this->askfor";       $V[$n][1] = "$this->askfor";       $n++;
		$V[$n][0] = "\$this->askformail";   $V[$n][1] = "$this->askformail";   $n++;
		$V[$n][0] = "\$this->askforname";   $V[$n][1] = "$this->askforname";   $n++;
		$V[$n][0] = "\$this->askforphone";  $V[$n][1] = "$this->askforphone";  $n++;
		$V[$n][0] = "\$this->author";       $V[$n][1] = "$this->author";       $n++;
		$V[$n][0] = "\$this->base";         $V[$n][1] = "$this->base";         $n++;
		$V[$n][0] = "\$this->bgcolor";      $V[$n][1] = "$this->bgcolor";      $n++;
		$V[$n][0] = "\$this->ca";           $V[$n][1] = "$this->ca";           $n++;
		$V[$n][0] = "\$this->capability";   $V[$n][1] = "$this->capability";   $n++;
		$V[$n][0] = "\$this->charset";      $V[$n][1] = "$this->charset";      $n++;
		$V[$n][0] = "\$this->checkvers";    $V[$n][1] = "$this->checkvers";    $n++;
		$V[$n][0] = "\$this->curver";       $V[$n][1] = "$this->curver";       $n++;
		$V[$n][0] = "\$this->date";         $V[$n][1] = "$this->date";         $n++;
		$V[$n][0] = "\$this->drawchart";    $V[$n][1] = "$this->drawchart";    $n++;
		$V[$n][0] = "\$this->drawsheet";    $V[$n][1] = "$this->drawsheet";    $n++;
		$V[$n][0] = "\$this->duration";     $V[$n][1] = "$this->duration";     $n++;
		$V[$n][0] = "\$this->email";        $V[$n][1] = "$this->email";        $n++;
		$V[$n][0] = "\$this->file";         $V[$n][1] = "$this->file";         $n++;
		$V[$n][0] = "\$this->flagnew";      $V[$n][1] = "$this->flagnew";      $n++;
		$V[$n][0] = "\$this->fname";        $V[$n][1] = "$this->fname";        $n++;
		$V[$n][0] = "\$this->goal";         $V[$n][1] = "$this->goal";         $n++;
		$V[$n][0] = "\$this->grext";        $V[$n][1] = "$this->grext";        $n++;
		$V[$n][0] = "\$this->hmdetails";    $V[$n][1] = "$this->hmdetails";    $n++;
		$V[$n][0] = "\$this->host";         $V[$n][1] = "$this->host";         $n++;
		$V[$n][0] = "\$this->ia";           $V[$n][1] = "$this->ia";           $n++;
		$V[$n][0] = "\$this->idqcm";        $V[$n][1] = "$this->idqcm";        $n++;
		$V[$n][0] = "\$this->imgfilename";  $V[$n][1] = "$this->imgfilename";  $n++;
		$V[$n][0] = "\$this->indent";       $V[$n][1] = "$this->indent";       $n++;
		$V[$n][0] = "\$this->indentsize";   $V[$n][1] = "$this->indentsize";   $n++;
		$V[$n][0] = "\$this->insertpoint";  $V[$n][1] = "$this->insertpoint";  $n++;
		$V[$n][0] = "\$this->itq";          $V[$n][1] = "$this->itq";          $n++;
		$V[$n][0] = "\$this->lname";        $V[$n][1] = "$this->lname";        $n++;
		$V[$n][0] = "\$this->login";        $V[$n][1] = "$this->login";        $n++;
		$V[$n][0] = "\$this->logopos";      $V[$n][1] = "$this->logopos";      $n++;
		$V[$n][0] = "\$this->logosrc";      $V[$n][1] = "$this->logosrc";      $n++;
		$V[$n][0] = "\$this->mailto";       $V[$n][1] = "$this->mailto";       $n++;
		$V[$n][0] = "\$this->maxpoints";    $V[$n][1] = "$this->maxpoints";    $n++;
		$V[$n][0] = "\$this->maxtime";      $V[$n][1] = "$this->maxtime";      $n++;
		$V[$n][0] = "\$this->media";        $V[$n][1] = "$this->media";        $n++;
		$V[$n][0] = "\$this->mintime";      $V[$n][1] = "$this->mintime";      $n++;
		$V[$n][0] = "\$this->na";           $V[$n][1] = "$this->na";           $n++;
		$V[$n][0] = "\$this->nbc";          $V[$n][1] = "$this->nbc";          $n++;
		$V[$n][0] = "\$this->nbq";          $V[$n][1] = "$this->nbq";          $n++;
		$V[$n][0] = "\$this->org";          $V[$n][1] = "$this->org";          $n++;
		$V[$n][0] = "\$this->outtime";      $V[$n][1] = "$this->outtime";      $n++;
		$V[$n][0] = "\$this->password";     $V[$n][1] = "$this->password";     $n++;
		$V[$n][0] = "\$this->patronym";     $V[$n][1] = "$this->patronym";     $n++;
		$V[$n][0] = "\$this->ponderation";  $V[$n][1] = "$this->ponderation";  $n++;
		$V[$n][0] = "\$this->port";         $V[$n][1] = "$this->port";         $n++;
		$V[$n][0] = "\$this->qcmrelease";   $V[$n][1] = "$this->qcmrelease";   $n++;
		$V[$n][0] = "\$this->qcmver";       $V[$n][1] = "$this->qcmver";       $n++;
		$V[$n][0] = "\$this->refver";       $V[$n][1] = "$this->refver";       $n++;
		$V[$n][0] = "\$this->release";      $V[$n][1] = "$this->release";      $n++;
		$V[$n][0] = "\$this->remoteaddr";   $V[$n][1] = "$this->remoteaddr";   $n++;
		$V[$n][0] = "\$this->save";         $V[$n][1] = "$this->save";         $n++;
		$V[$n][0] = "\$this->savefile";     $V[$n][1] = "$this->savefile";     $n++;
		$V[$n][0] = "\$this->score";        $V[$n][1] = "$this->score";        $n++;
		$V[$n][0] = "\$this->sendmail";     $V[$n][1] = "$this->sendmail";     $n++;
		$V[$n][0] = "\$this->sendsms";      $V[$n][1] = "$this->sendsms";      $n++;
		$V[$n][0] = "\$this->shownum";      $V[$n][1] = "$this->shownum";      $n++;
		$V[$n][0] = "\$this->showsender";   $V[$n][1] = "$this->showsender";   $n++;
		$V[$n][0] = "\$this->showserver";   $V[$n][1] = "$this->showserver";   $n++;
		$V[$n][0] = "\$this->showtitle";    $V[$n][1] = "$this->showtitle";    $n++;
		$V[$n][0] = "\$this->site";         $V[$n][1] = "$this->site";         $n++;
		$V[$n][0] = "\$this->smsto";        $V[$n][1] = "$this->smsto";        $n++;
		$V[$n][0] = "\$this->sqlserverset"; $V[$n][1] = "$this->sqlserverset"; $n++;
		$V[$n][0] = "\$this->start";        $V[$n][1] = "$this->start";        $n++;
		$V[$n][0] = "\$this->starton";      $V[$n][1] = "$this->starton";      $n++;
		$V[$n][0] = "\$this->stmail";       $V[$n][1] = "$this->stmail";       $n++;
		$V[$n][0] = "\$this->stop";         $V[$n][1] = "$this->stop";         $n++;
		$V[$n][0] = "\$this->stopon";       $V[$n][1] = "$this->stopon";       $n++;
		$V[$n][0] = "\$this->stsms";        $V[$n][1] = "$this->stsms";        $n++;
		$V[$n][0] = "\$this->table";        $V[$n][1] = "$this->table";        $n++;
		$V[$n][0] = "\$this->threshold";    $V[$n][1] = "$this->threshold";    $n++;
		$V[$n][0] = "\$this->title";        $V[$n][1] = "$this->title";        $n++;
		$V[$n][0] = "\$this->typechart";    $V[$n][1] = "$this->typechart";    $n++;
		$V[$n][0] = "\$this->urlback";      $V[$n][1] = "$this->urlback";      $n++;
		$V[$n][0] = "\$this->userfile";     $V[$n][1] = "$this->userfile";     $n++;
		$V[$n][0] = "\$this->userlist";     $V[$n][1] = "$this->userlist";     $n++;
		$V[$n][0] = "\$this->version";      $V[$n][1] = "$this->version";      $n++;

		echo "<hr size=\"4\" noshade><b>DEBUG</b>";
		echo "<style type=\"text/css\">";
		echo "td {font-family:arial;font-size:10pt}\n";
		echo ".t1 {background-color:#D0D0D0}\n";
		echo ".t2 {text-align:center;background-color:#FFFFA0}\n";
		echo "</style>";
		echo "<table border='1'>\n"; $n = 0;
		while($n<count($V)) {
			if($V[$n][1]) { $value = $V[$n][1]; } else { $value = "&nbsp;"; }
			$this->writeLine($V[$n][0],$value); $n++;
		}
		echo "<tr class=\"t1\"><td valign=\"top\"><b>\$this->categories[ ]</b></td><td>";
		if($this->nbc) {
			for($n=0; $n<$this->nbc; $n++) {
				echo $n.": ".$this->categories[$n][0]." ";
				echo "(".$this->categories[$n][3]." pts max)<br>";
			} }
		else {
			echo "&nbsp;";
		}
		echo "</td></tr>\n";
		echo "<tr class=\"t1\"><td valign=\"top\"><b>\$this->detail</b></td><td>";
		$cats = explode(",",$this->detail);
		for($n=0; $n<count($cats); $n++) { echo $cats[$n]."<br>"; }
		echo "</td></tr></table><br><pre>\n";
		echo "<table border=\"1\">";
		echo "<tr class=\"t2\">";
		echo "<td width=\"120\"><b>Catégorie</b></td>";
		echo "<td width=\"120\"><b>nb Questions</b></td>";
		echo "<td width=\"120\"><b>Bonnes rép.</b></td>";
		echo "<td width=\"120\"><b>points maxi</b></td>";
		echo "<td width=\"120\"><b>points acquis</b></td>";
		echo "</tr>\n";
		for($n=0; $n<$this->nbc; $n++) {
			$c0 = $this->categories[$n][0];
			$c1 = "&nbsp;"; $c2 = "&nbsp;"; $c3 = "&nbsp;"; $c4 = "&nbsp;";
			if($this->categories[$n][1]) { $c1 = $this->categories[$n][1]; }
			if($this->categories[$n][2]) { $c2 = $this->categories[$n][2]; }
			if($this->categories[$n][3]) { $c3 = $this->categories[$n][3]; }
			if($this->categories[$n][4]) { $c4 = $this->categories[$n][4]; }
			echo "<tr align=\"center\">";
			echo "<td align=\"left\">$c0</td>";
			echo "<td>$c1</td>";
			echo "<td>$c2</td>";
			echo "<td>$c3</td>";
			echo "<td>$c4</td>";
			echo "</tr>\n";
		}
		echo "</table><p>\n";

		echo "<b>\$this->forfile</b>:\n$this->forfile\n";
		echo "<b>\$this->forsql</b>:\n$this->forsql\n";
		echo "<b>\$this->forsms</b>:\n$this->forsms\n";
		echo "<b>\$this->formail</b>:\n$this->formail";
		echo "<hr size=\"4\" noshade></pre>";
	}

	// Write a table row
	function writeLine($clef,$value) {
		echo "<tr class=\"t1\"><td><b>$clef</b></td><td>$value</td></tr>\n";
	}

	// Write the questions and categories only
	function showQuestions() {
		echo "<table border=\"1\">\n";
		for($n=0; $n<count($this->quest); $n++) {
			$proptab = explode(";",$this->quest[$n][0]); $theme = $proptab[1]-1;
			echo "<tr>";
			echo "<td>".$this->categories[$theme][0]."</td>";
			if($this->nbc) {
				echo "<td>".$this->quest[$n][1]."</td>";
			}
			echo "</tr>\n";
		}
		echo "</table>\n";
	}

	// Show the responses
	function showResponses() {
		echo "$this->t_yourreplies<br><br>";
		for($n=0; $n<count($this->itemsdetails); $n++) {
			for($m=0; $m<count($this->itemsdetails[$n]); $m++) {
				if($m==0) { echo ($n+1).". "; }
				echo $this->itemsdetails[$n][$m]."<br>";
			}
			echo "<hr>";
		}
	}

	//  #################################################################
	//
	//  <DELETE>
	//
	//  TECHNICAL COMMENTS
	//  ------------------
	//
	//  The different items are stored in an array (var $quest = array();)
	//  The structure of this array is shown as below :
	//
	//  array (
	//      0 => array (
	//               0 => "item properties",
	//               1 => "text of the question 1",
	//               2..n => "proposals"
	//           ),
	//      1 => array (
	//               0 => "item properties",
	//               1 => "text of the question 2",
	//               2..n => "proposals"
	//           ),
	//      n => array (
	//               ...
	//           )
	//  )
	//
	//  The categories are stored in an array (var $categories = array();)
	//  Each line of this array is shown as below :
	//
	//                           [0]          [1]       [2]       [3]        [4]
	//                   +----------------+----------+-------+------------+--------+
	//  $categories[0] : | Categorie text | nb Quest | nb CR | Max points | Points |
	//                   +----------------+----------+-------+------------+--------+
	//  $categories[1] : |             ...
	//                   +-----------
	//  Col 0 : Text of the category
	//  Col 1 : Number of questions in that category
	//  Col 2 : Number of correct responses in that category
	//  Col 3 : Number maximum of points which can be obtained in that category
	//  Col 4 : Number of points obtained in that category
	//
	//  Table structure if a database is used. (The table name can be different)
	//  ------------------------------------------------------------------------
	//
	//  drop table if exists RTests;
	//  create table RTests (
	//    idtest integer not null auto_increment,
	//    date datetime default '0000-00-00 00:00:00',   # Date of the test
	//    lname varchar(25) default '',                  # Last name of the student
	//    fname varchar(30) default '',                  # First name of the student
	//    email varchar(50) default '',                  # Email of the student
	//    tel varchar(10) default '',                    # Phone number of the student
	//    raddr varchar(15) default '0.0.0.0',           # IP address of the student (remote address)
	//    idqcm mediumint default '0',                   # id of the QCM
	//    version varchar(6) default '',                 # Version of the QCM
	//    duration varchar(8) default '00:00:00',        # Duration of the test online
	//    nbq tinyint default '0',                       # Number of questions in the QCM
	//    nbca tinyint default '0',                      # Number of correct answers
	//    nbia tinyint default '0',                      # Number of incorrect answers
	//    nbna tinyint default '0',                      # Number of no answer
	//    skills decimal(6,2) default 0.00,              # Capability of the student
	//    detail mediumtext default '',                  # Details of results in categories
	//    fdetails mediumtext default '',                # Full details: item by item
	//    primary key (idtest)
	//  ) type=MyISAM comment='Table of tests online results';
	//
	//
	//  Methods tree
	//  ------------
	//
	//  a) First call : display the test online
	//
	//  +-C--qcm()                                Constructor: $var = new qcm(17);
	//  |     +----read()
	//  |     |    +----isComment()
	//  |     |    +----header()
	//  |     |    |    `----calcTime()
	//  |     |    `----verifCoherence()
	//  |     +----canWeDoIt()
	//  |     +----notNow()
	//  |     `----imgFilename()
	//  `----display()                            <== CALL METHOD : $var->display();
	//        +----writeLogo()
	//        +----writeTitle()
	//        +----getStudentInfos()
	//        |     `----tr()
	//        +----writeQCMRU()
	//        +----writeQCMRM()
	//        +----writeQCMTR()
	//        +----writeQCMRE()
	//        +----writeQCMSE()
	//        +----writeQCMRO()
	//        +----writeLogo()
	//        `----isObsolete()
	//
	//   b) Secund call : correct the test online
	//
	//   methods()                                variables obtained or <action>
	//  ===========================================================================
	//
	//  +-C--qcm()                                => lname,fname,email,tel,raddr
	//  |     +----read()                         => version,nbq
	//  |     |    +----isComment()
	//  |     |    +----header()
	//  |     |    |    `----calcTime()
	//  |     |    `----verifCoherence()          <verify the coherence of the header QCM file>
	//  |     +----canWeDoIt()
	//  |     +----notNow()
	//  |     `----imgFilename()
	//  +----correction()                         <== CALL METHOD : $var->correction();
	//        +----correct()                      => date,nbca,nbia,nbna,skills,details
	//        |     +----writeTitle()
	//        |     +----duration()               => duration
	//        |     |       `----hms2s()          <convert HH:MM:SS into seconds>
	//        |     +----evalUniqueResponse()
	//        |     `----evalMultipleResponse()
	//        +----prepareAll()                   <prepare all we have to do>
	//        |     +----prepareFile()            => forfile    if asked
	//        |     +----prepareSQL()             => forsql         "
	//        |     |       `----reportAllItems() <return all questions and replies if asked>
	//        |     +----prepareMail()            => formail        "
	//        |     |       `----reportAllItems() <return all questions and replies if asked>
	//        |     `----prepareSMS()             => forsms         "
	//        +----graph()
	//        |     +----draw_lines()             <draw lines graph if asked>
	//        |     +----draw_histo()             <draw histo graph    "    >
	//        |     `----draw_pie()               <draw pie graph      "    >
	//        |           +----pie_chart()
	//        |           `----portion()
	//        +----sheet()                        <draw a statistic sheet if asked>
	//        +----save()                         <save on the selected media if asked>
	//        |     `----execRequete()
	//        +----sendSMS()                      <send an SMS if asked>
	//        +----sendMail()                     <send a mail     "   >
	//        |     +----isObsolete()             <check for a new version of QCM Class>
	//        |     `----reportGoodResponses()    <send the correction to student is asked by details=full>
	//        `----gotoURL()                      <button to an URL if urlback is defined>
	//
	//  </DELETE>
	// --------------------------------------------------------------------------------
	// Versions:
	// 0.1  (xx/02/2002) : Original version. Only one type of item : QCMRU
	// 0.2  => 1.8       : Differents version in use while working on it.
	// 1.9  (05/03/2004) : Four types of item is now available (QCMRU, QCMRM, Sort, Associations)
	// 2.0  (01/04/2004) : First public released version
	// 2.1  (09/05/2004) : The starton,stopon,sqlserver parameters are now possible in the qcm file
	// 2.2  (06/09/2004) : The mintime,maxtime parameters are possible in the qcm file.
	// 2.3  (16/09/2004) : The shownum parameter is possible in the qcm file to show the item number
	// 2.4  (04/10/2004) : People can now pick their name in a combo list (parameter: userlist)
	// 2.5  (09/10/2004) : Comments can be used. They begin by ; as well as #
	// 2.6  (20/10/2005) : The goal and details parameters are possible to say how to have the replies
	// 2.7  (01/12/2005) : New type of item (n° 4). We can select an element to complete a proposal
	// 2.8  (21/03/2006) : The class can be used now with several languages (using a language file)
	// 2.9  (24/03/2006) : Correction of a bug upon the test of the PHP version (from: hide@address.com)
	// 2.10 (25/08/2007) : Add a new type of item : QROC (in french: Question with Short Open Response)
	// 2.11 (27/08/2007) : Bug correction when the value 'large' was used for the 'details' key.
	// 2.12 (11/10/2007) : New header parameter (showsender). Send or not the sender address (IP or hostname)
	// 2.13 (18/03/2009) : Bug correction using the type n° 4 of item and with register_globals = Off (php.ini)
	// 2.14 (26/03/2009) : Bug correction (graph. filename) and new header parameters (charset, showserver, logo)
	// 2.15 (31/03/2009) : New header parameter, (checkvers, flagnew) to alert if a new version exists
	// 2.16 (03/04/2009) : New value for the parameter details : 'full' to send the correction. Student only.
	//
	// Thank your for your feedback at hide@address.com or http://www.fr-webdev.net/
	//
	// --------------------------------------------------------------------------------
	// Your contributions:
	// 24/03/2006       : Florent OUENADIO, <hide@address.com>.
	//                  : Report a bug: incorrect test of the PHP version.
	// --------------------------------------------------------------------------------

}
?>
Return current item: QCM Class