<?
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 " " 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(" ", $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]." ";
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]." ".date("d/m/Y H:i",$this->starton);
break;
case 3:
echo $this->t_end[$this->itq]." ".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\"> \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 = "© <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 = "=>"; $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> $this->t_nbpoints </td>";
echo "<td align=\"right\">$this->score $this->t_on ";
echo "$this->maxpoints </td></tr>\n";
}
echo "<tr><td> $this->t_nbquestions </td>";
echo "<td align=\"right\">$this->nbq </td></tr>\n";
echo "<tr><td> $this->t_nbcresponses </td>";
echo "<td align=\"right\">$this->ca </td></tr>\n";
echo "<tr><td> $this->t_nbiresponses </td>";
echo "<td align=\"right\">$this->ia </td></tr>\n";
echo "<tr><td> $this->t_nbnresponses </td>";
echo "<td align=\"right\">$this->na </td></tr>\n";
echo "<tr><td> ".$this->t_playtime[$this->itq]." </td>";
echo "<td align=\"right\">$this->duration </td></tr>\n";
echo "<tr bgcolor=\"#FFFF80\"><td> $this->t_competence </td>";
echo "<td align=\"right\">$this->capability % </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] . " </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 " </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 ";
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 "© $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>"
. "© $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 " ";
echo " ";
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 = " "; }
$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 " ";
}
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 = " "; $c2 = " "; $c3 = " "; $c4 = " ";
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.
// --------------------------------------------------------------------------------
}
?>