1<?php
2// (c) Copyright by authors of the Tiki Wiki CMS Groupware Project
3//
4// All Rights Reserved. See copyright.txt for details and a complete list of authors.
5// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details.
6// $Id$
7
8//this script may only be included - so its better to die if called directly.
9if (strpos($_SERVER["SCRIPT_NAME"], basename(__FILE__)) !== false) {
10	header("location: index.php");
11	exit;
12}
13
14/**
15 *
16 */
17class QuizLib extends TikiLib
18{
19	/**
20	 * @param $quizId
21	 * @return bool
22	 */
23	public function get_quiz($quizId)
24	{
25		$query = "select * from `tiki_quizzes` where `quizId`=?";
26
27		$result = $this->query($query, [(int) $quizId]);
28
29		if (! $result->numRows()) {
30			return false;
31		}
32
33		$res = $result->fetchRow();
34		return $res;
35	}
36
37	public function compute_quiz_stats()
38	{
39		$query = "select `quizId`  from `tiki_user_quizzes`";
40
41		$result = $this->fetchAll($query, []);
42
43		$quizStatsSum = $this->table('tiki_quiz_stats_sum');
44
45		foreach ($result as $res) {
46			$quizId = $res["quizId"];
47
48			$quizName = $this->getOne("select `name`  from `tiki_quizzes` where `quizId`=?", [(int) $quizId]);
49			$timesTaken = $this->getOne("select count(*) from `tiki_user_quizzes` where `quizId`=?", [(int) $quizId]);
50			$avgpoints = $this->getOne("select avg(`points`) from `tiki_user_quizzes` where `quizId`=?", [(int) $quizId]);
51			$maxPoints = $this->getOne("select max(`maxPoints`) from `tiki_user_quizzes` where `quizId`=?", [(int) $quizId]);
52			$avgavg = ($maxPoints != 0) ? $avgpoints / $maxPoints * 100 : 0.0;
53			$avgtime = $this->getOne("select avg(`timeTaken`) from `tiki_user_quizzes` where `quizId`=?", [(int) $quizId]);
54
55			$quizStatsSum->delete(['quizId' => (int) $quizId,]);
56			$quizStatsSum->insert(
57				[
58					'quizId' => (int) $quizId,
59					'quizName' => $quizName,
60					'timesTaken' => (int) $timesTaken,
61					'avgpoints' => (float) $avgpoints,
62					'avgtime' => $avgtime,
63					'avgavg' => $avgavg,
64				]
65			);
66		}
67	}
68
69	/**
70	 * @param $offset
71	 * @param $maxRecords
72	 * @param string $sort_mode
73	 * @param null $find
74	 * @return array
75	 */
76	public function list_quizzes($offset, $maxRecords, $sort_mode = 'name_desc', $find = null)
77	{
78
79		$quizzes = $this->table('tiki_quizzes');
80		$conditions = [];
81
82		if (! empty($find)) {
83			$findesc = '%' . $find . '%';
84			$conditions['search'] = $quizzes->expr('(`name` like ? or `description` like ?)', [$findesc, $findesc]);
85		}
86
87		$result = $quizzes->fetchColumn('quizId', $conditions);
88		$res = $ret = $retids = [];
89		$n = 0;
90
91		//FIXME Perm:filter ?
92		foreach ($result as $res) {
93			$objperm = Perms::get('quizzes', $res);
94
95			if ($objperm->take_quiz) {
96				if (($maxRecords == -1) || (($n >= $offset) && ($n < ($offset + $maxRecords)))) {
97					$retids[] = $res;
98				}
99				$n++;
100			}
101		}
102
103		if ($n > 0) {
104			$result = $quizzes->fetchAll(
105				$quizzes->all(),
106				['quizId' => $quizzes->in($retids)],
107				-1,
108				-1,
109				$quizzes->expr($this->convertSortMode($sort_mode))
110			);
111
112			$questions = $this->table('tiki_quiz_questions');
113			$results = $this->table('tiki_quiz_results');
114
115			foreach ($result as $res) {
116				$res['questions'] = $questions->fetchCount(['quizId' => (int) $res['quizId']]);
117				$res['results'] = $results->fetchCount(['quizId' => (int) $res['quizId']]);
118				$ret[] = $res;
119			}
120		}
121
122		return [
123			'data' => $ret,
124			'cant' => $n,
125		];
126	}
127
128	/**
129	 * @param $userResultId
130	 * @return bool
131	 */
132	public function get_user_quiz_result($userResultId)
133	{
134		$query = "select * from `tiki_user_quizzes` where `userResultId`=?";
135
136		$result = $this->query($query, [$userResultId]);
137
138		if (! $result->numRows()) {
139			return false;
140		}
141
142		$res = $result->fetchRow();
143		return $res;
144	}
145
146	/**
147	 * @param $quizId
148	 * @param int $offset
149	 * @param $maxRecords
150	 * @param string $sort_mode
151	 * @param string $find
152	 * @return array
153	 */
154	public function list_quiz_question_stats($quizId, $offset = 0, $maxRecords = -1, $sort_mode = 'position_asc', $find = '')
155	{
156
157		$query = "select distinct(tqs.`questionId`)"
158						. " from `tiki_quiz_stats` tqs,`tiki_quiz_questions` tqq"
159						. " where tqs.`questionId`=tqq.`questionId` and tqs.`quizId` = ? order by "
160						. $this->convertSortMode($sort_mode);
161
162		$result = $this->query($query, [(int) $quizId]);
163		$ret = [];
164
165		while ($res = $result->fetchRow()) {
166			$question = $this->getOne("select `question` from `tiki_quiz_questions` where `questionId`=?", [(int) $res["questionId"]]);
167
168			$total_votes = $this->getOne(
169				"select sum(`votes`) from `tiki_quiz_stats` where `quizId`=? and `questionId`=?",
170				[(int) $quizId, (int) $res["questionId"]]
171			);
172			$query2 = "select tqq.`optionId`,`votes`,`optionText`"
173								. " from `tiki_quiz_stats` tqq,`tiki_quiz_question_options` tqo"
174								. " where tqq.`optionId`=tqo.`optionId` and tqq.`questionId`=?"
175								;
176			$result2 = $this->query($query2, [(int) $res["questionId"]]);
177			$options = [];
178
179			while ($res = $result2->fetchRow()) {
180				$opt = [];
181
182				$opt["optionText"] = $res["optionText"];
183				$opt["votes"] = $res["votes"];
184				$opt["avg"] = $res["votes"] / $total_votes * 100;
185				$options[] = $opt;
186			}
187
188			$ques = [];
189			$ques["options"] = $options;
190			$ques["question"] = $question;
191			$ret[] = $ques;
192		}
193
194		return $ret;
195	}
196
197	/**
198	 * @param $answerUploadId
199	 */
200	public function download_answer($answerUploadId)
201	{
202
203		$query = "SELECT `filecontent`, `filetype`, `filename`, `filesize` FROM `tiki_user_answers_uploads` WHERE `answerUploadId`=?";
204
205		$result = $this->query($query, [(int) $answerUploadId]);
206		$ret = [];
207
208		while ($res = $result->fetchRow()) {
209			$data = $res['filecontent'];
210			$name = $res['filename'];
211			$type = $res['filetype'];
212			$size = $res['filesize'];
213		}
214
215		$name = htmlspecialchars($name);
216
217		header("Content-type: $type");
218		header("Content-length: $size");
219		header("Content-Disposition: attachment; filename=\"$name\"");
220		header("Content-Description: PHP Generated Data");
221		print $data;
222	}
223
224
225	/**
226	 * @param $userResultId
227	 * @return array
228	 */
229	public function get_user_quiz_questions($userResultId)
230	{
231		$query = "select distinct(tqs.`questionId`) from `tiki_user_answers` tqs,`tiki_quiz_questions` tqq"
232						. " where tqs.`questionId`=tqq.`questionId` and tqs.`userResultId` = ? order by `position` desc";
233
234		$result = $this->query($query, [(int) $userResultId]);
235		$ret = [];
236
237		while ($res = $result->fetchRow()) {
238			$question = $this->getOne("select `question` from `tiki_quiz_questions` where `questionId`=?", [(int) $res["questionId"]]);
239
240			$questionId = $res["questionId"];
241
242			$query2 = "select tqq.`optionId`,tqo.`points`,`optionText`"
243								. " from `tiki_user_answers` tqq,`tiki_quiz_question_options` tqo"
244								. " where tqq.`optionId`=tqo.`optionId` and tqq.`userResultId`=? and tqq.`questionId`=?";
245			$result2 = $this->query($query2, [(int) $userResultId, (int) $questionId]);
246			$options = [];
247
248			while ($res = $result2->fetchRow()) {
249				$opt = [];
250
251				$opt["optionText"] = $res["optionText"];
252				$opt["points"] = $res["points"];
253
254				$query3 = "select `answerUploadId`, `filename` from `tiki_user_answers_uploads` where `userResultId` = ? and `questionId` = ?";
255				$result3 = $this->query($query3, [(int) $userResultId, (int) $questionId]);
256
257				while ($res2 = $result3->fetchRow()) {
258					$opt["filename"] = $res2["filename"];
259					$opt["answerUploadId"] = $res2["answerUploadId"];
260				}
261
262				$options[] = $opt;
263			}
264
265
266			$ques = [];
267			$ques["options"] = $options;
268			$ques["question"] = $question;
269			$ret[] = $ques;
270		}
271
272
273		return $ret;
274	}
275
276	/**
277	 * @param $userResultId
278	 */
279	public function remove_quiz_stat($userResultId)
280	{
281		$query = "select `quizId`,`user` from `tiki_user_quizzes` where `userResultId`=?";
282		$bindvars = [(int) $userResultId];
283
284		$result = $this->query($query, $bindvars);
285		$res = $result->fetchRow();
286		$user = $res["user"];
287		$quizId = $res["quizId"];
288
289		$query = "delete from `tiki_user_taken_quizzes` where `user`=? and `quizId`=?";
290		$result = $this->query($query, [$user, (int) $quizId]);
291
292		$query = "delete from `tiki_user_quizzes` where `userResultId`=?";
293		$result = $this->query($query, $bindvars);
294		$query = "delete from `tiki_user_answers` where `userResultId`=?";
295		$result = $this->query($query, $bindvars);
296	}
297
298	/**
299	 * @param $quizId
300	 */
301	public function clear_quiz_stats($quizId)
302	{
303		$query = "delete from `tiki_user_taken_quizzes` where `quizId`=?";
304		$bindvars = [(int) $quizId];
305
306		$result = $this->query($query, $bindvars);
307
308		$query = "delete from `tiki_quiz_stats_sum` where `quizId`=?";
309		$result = $this->query($query, $bindvars);
310
311		$query = "delete from `tiki_quiz_stats` where `quizId`=?";
312		$result = $this->query($query, $bindvars);
313
314		$query = "delete from `tiki_user_quizzes` where `quizId`=?";
315		$result = $this->query($query, $bindvars);
316
317		$query = "delete from `tiki_user_answers` where `quizId`=?";
318		$result = $this->query($query, $bindvars);
319	}
320
321	/**
322	 * @param $quizId
323	 * @param $offset
324	 * @param $maxRecords
325	 * @param $sort_mode
326	 * @param $find
327	 * @return array
328	 */
329	public function list_quiz_stats($quizId, $offset, $maxRecords, $sort_mode, $find)
330	{
331		$this->compute_quiz_stats();
332
333		$query = "select `passingperct` from `tiki_quizzes` where `quizId` = ?";
334		$passingperct = $this->getOne($query, [(int) $quizId]);
335
336		if ($find) {
337			//isnt that superflous? hmm.
338			$findesc = '%' . $find . '%';
339		}
340		$mid = " where `quizId`=?";
341		$bindvars = [(int) $quizId];
342
343		$query = "select * from `tiki_user_quizzes` $mid order by " . $this->convertSortMode($sort_mode);
344		$query_cant = "select count(*) from `tiki_user_quizzes` $mid";
345		$result = $this->query($query, $bindvars, $maxRecords, $offset);
346		$cant = $this->getOne($query_cant, $bindvars);
347		$ret = [];
348
349		while ($res = $result->fetchRow()) {
350			$res["avgavg"] = ($res["maxPoints"] != 0) ? $res["points"] / $res["maxPoints"] * 100 : 0.0;
351
352			if (isset($passingperct) && $passingperct > 0) {
353				$res['ispassing'] = ($res["avgavg"] >= $passingperct) ? true : false;
354			}
355
356			$hasDet = $this->getOne(
357				"select count(*) from `tiki_user_answers` where `userResultId`=?",
358				[(int) $res["userResultId"]]
359			);
360			if ($hasDet) {
361				$res["hasDetails"] = 'y';
362			} else {
363				$res["hasDetails"] = 'n';
364			}
365
366			$ret[] = $res;
367		}
368
369		$retval = [];
370		$retval["data"] = $ret;
371		$retval["cant"] = $cant;
372		return $retval;
373	}
374
375	/**
376	 * @param $offset
377	 * @param $maxRecords
378	 * @param $sort_mode
379	 * @param $find
380	 * @return array
381	 */
382	public function list_quiz_sum_stats($offset, $maxRecords, $sort_mode, $find)
383	{
384		$this->compute_quiz_stats();
385
386		$stats = $this->table('tiki_quiz_stats_sum');
387		$conditions = [];
388
389		if ($find) {
390			$conditions['quizName'] = $stats->like("%$find%");
391		}
392
393		return [
394			'data' => $stats->fetchAll($stats->all(), $conditions, $maxRecords, $offset, $stats->expr($this->convertSortMode($sort_mode))),
395			'cant' => $stats->fetchCount($conditions),
396		];
397	}
398
399
400
401	// Takes a given uploaded answer and inserts it into the DB. - burley
402	/**
403	 * @param $userResultId
404	 * @param $questionId
405	 * @param $filename
406	 * @param $filetype
407	 * @param $filesize
408	 * @param $tmp_name
409	 */
410	public function register_user_quiz_answer_upload($userResultId, $questionId, $filename, $filetype, $filesize, $tmp_name)
411	{
412
413		$data = fread(fopen($tmp_name, "r"), filesize($tmp_name));
414
415		$query = "insert into `tiki_user_answers_uploads`"
416							. "(`userResultId`,`questionId`,`filename`,`filetype`,`filesize`,`filecontent`)"
417							. " values(?,?,?,?,?,?)";
418		$result = $this->query($query, [(int) $userResultId, (int) $questionId, $filename, $filetype, $filesize, $data]);
419	}
420
421
422	/**
423	 * @param $userResultId
424	 * @param $quizId
425	 * @param $questionId
426	 * @param $optionId
427	 */
428	public function register_user_quiz_answer($userResultId, $quizId, $questionId, $optionId)
429	{
430		$query = "insert into `tiki_user_answers`(`userResultId`,`quizId`,`questionId`,`optionId`) values(?,?,?,?)";
431		$result = $this->query($query, [(int) $userResultId, (int) $quizId, (int) $questionId, (int) $optionId]);
432	}
433
434	/**
435	 * @param $quizId
436	 * @param $user
437	 * @param $timeTaken
438	 * @param $points
439	 * @param $maxPoints
440	 * @param $resultId
441	 * @return mixed
442	 */
443	public function register_quiz_stats($quizId, $user, $timeTaken, $points, $maxPoints, $resultId)
444	{
445		// Fix a bug if no result is indicated.
446		if (! $resultId) {
447			$resultId = 0;
448		}
449
450		$query = "insert into `tiki_user_quizzes`(`user`,`quizId`,`timestamp`,`timeTaken`,`points`,`maxPoints`,`resultId`) values(?,?,?,?,?,?,?)";
451		$result = $this->query(
452			$query,
453			[
454				$user,
455				$quizId,
456				$this->now,
457				$timeTaken,
458				$points,
459				$maxPoints,
460				$resultId
461			]
462		);
463		$queryId = $this->getOne(
464			"select max(`userResultId`) from `tiki_user_quizzes` where `timestamp`=? and `quizId`=?",
465			[$this->now, (int) $quizId]
466		);
467		return $queryId;
468	}
469
470	/**
471	 * @param $quizId
472	 * @param $questionId
473	 * @param $optionId
474	 * @return bool
475	 */
476	public function register_quiz_answer($quizId, $questionId, $optionId)
477	{
478		$cant = $this->getOne(
479			"select count(*) from `tiki_quiz_stats` where `quizId`=? and `questionId`=? and `optionId`=?",
480			[(int) $quizId, (int) $questionId, (int) $optionId]
481		);
482
483		if ($cant) {
484			$query = "update `tiki_quiz_stats` set `votes`=`votes`+1 where `quizId`=? and `questionId`=? and `optionId`=?";
485			$bindvars = [(int) $quizId, (int) $questionId, (int) $optionId];
486		} else {
487			$query = "insert into `tiki_quiz_stats`(`quizId`,`questionId`,`optionId`,`votes`) values(?,?,?,?)";
488			$bindvars = [(int) $quizId, (int) $questionId, (int) $optionId,1];
489		}
490
491		$result = $this->query($query, $bindvars);
492
493		return true;
494	}
495
496	/**
497	 * @param $quizId
498	 * @param $points
499	 * @return int
500	 */
501	public function calculate_quiz_result($quizId, $points)
502	{
503		$query = "select * from `tiki_quiz_results` where `fromPoints`<=? and `toPoints`>=? and `quizId`=?";
504
505		$result = $this->query($query, [(int) $points, (int) $points, (int) $quizId]);
506
507		if (! $result->numRows()) {
508			return 0;
509		}
510
511		$res = $result->fetchRow();
512		return $res;
513	}
514
515	/**
516	 * @param $user
517	 * @param $quizId
518	 * @return mixed
519	 */
520	public function user_has_taken_quiz($user, $quizId)
521	{
522		$cant = $this->getOne("select count(*) from `tiki_user_taken_quizzes` where `user`=? and `quizId`=?", [$user, (int) $quizId]);
523
524		return $cant;
525	}
526
527	/**
528	 * @param $user
529	 * @param $quizId
530	 */
531	public function user_takes_quiz($user, $quizId)
532	{
533		$query = "delete from `tiki_user_taken_quizzes` where `user`=? and `quizId`=?";
534		$bindvars = [$user,(int) $quizId];
535		$result = $this->query($query, $bindvars, -1, -1, false);
536		$query = "insert into `tiki_user_taken_quizzes`(`user`,`quizId`) values(?,?)";
537		$result = $this->query($query, $bindvars);
538	}
539
540	/**
541	 * @param $resultId
542	 * @param $quizId
543	 * @param $fromPoints
544	 * @param $toPoints
545	 * @param $answer
546	 * @return mixed
547	 */
548	public function replace_quiz_result($resultId, $quizId, $fromPoints, $toPoints, $answer)
549	{
550		if ($resultId) {
551			// update an existing quiz
552			$query = "update `tiki_quiz_results` set `fromPoints` = ?, `toPoints` = ?, `quizId` = ?, `answer` = ?  where `resultId` = ?";
553			$bindvars = [(int) $fromPoints,(int) $toPoints, (int) $quizId, $answer, (int) $resultId];
554			$result = $this->query($query, $bindvars);
555		} else {
556			// insert a new quiz
557
558			$query = "insert into `tiki_quiz_results`(`quizId`,`fromPoints`,`toPoints`,`answer`) values(?,?,?,?)";
559			$bindvars = [(int) $quizId, (int) $fromPoints, (int) $toPoints, $answer];
560			$result = $this->query($query, $bindvars);
561			$queryid = "select max(`resultId`) from `tiki_quiz_results` where `fromPoints`=? and `toPoints`=? and `quizId`=?";
562			$quizId = $this->getOne($queryid, [(int) $fromPoints, (int) $toPoints, $quizId]);
563		}
564
565		return $quizId;
566	}
567
568	/**
569	 * @param $resultId
570	 * @return bool
571	 */
572	public function get_quiz_result($resultId)
573	{
574		$query = "select * from `tiki_quiz_results` where `resultId`=?";
575
576		$result = $this->query($query, [(int) $resultId]);
577
578		if (! $result->numRows()) {
579			return false;
580		}
581
582		$res = $result->fetchRow();
583		return $res;
584	}
585
586	/**
587	 * @param $resultId
588	 * @return bool
589	 */
590	public function remove_quiz_result($resultId)
591	{
592		$query = "delete from `tiki_quiz_results` where `resultId`=?";
593
594		$result = $this->query($query, [$resultId]);
595		return true;
596	}
597
598	/**
599	 * @param $quizId
600	 * @param $offset
601	 * @param $maxRecords
602	 * @param $sort_mode
603	 * @param $find
604	 * @return array
605	 */
606	public function list_quiz_results($quizId, $offset, $maxRecords, $sort_mode, $find)
607	{
608
609		if ($find) {
610			$findesc = '%' . $find . '%';
611
612			$mid = " where `quizId`=? and `answer` like ? ";
613			$bindvars = [(int) $quizId, $findesc];
614		} else {
615			$mid = " where `quizId`=? ";
616			$bindvars = [(int) $quizId];
617		}
618
619		$query = "select * from `tiki_quiz_results` $mid order by " . $this->convertSortMode($sort_mode);
620		$query_cant = "select count(*) from `tiki_quiz_results` $mid";
621		$result = $this->query($query, $bindvars, $maxRecords, $offset);
622		$cant = $this->getOne($query_cant, $bindvars);
623		$ret = [];
624
625		while ($res = $result->fetchRow()) {
626			$ret[] = $res;
627		}
628
629		$retval = [];
630		$retval["data"] = $ret;
631		$retval["cant"] = $cant;
632		return $retval;
633	}
634
635	// called by tiki-edit_quiz.php
636	/**
637	 * @param $quizId
638	 * @param $name
639	 * @param $description
640	 * @param $canRepeat
641	 * @param $storeResults
642	 * @param $immediateFeedback
643	 * @param $showAnswers
644	 * @param $shuffleQuestions
645	 * @param $shuffleAnswers
646	 * @param $questionsPerPage
647	 * @param $timeLimited
648	 * @param $timeLimit
649	 * @param $publishDate
650	 * @param $expireDate
651	 * @param $passingperct
652	 * @return mixed
653	 */
654	public function replace_quiz($quizId, $name, $description, $canRepeat, $storeResults, $immediateFeedback, $showAnswers,	$shuffleQuestions, $shuffleAnswers, $questionsPerPage, $timeLimited, $timeLimit, $publishDate, $expireDate, $passingperct
655			)
656	{
657		if ($quizId) {
658			// update an existing quiz
659			$query = "update `tiki_quizzes` set `name` = ?, `description` = ?, `canRepeat` = ?, `storeResults` = ?,";
660			$query .= "`immediateFeedback` = ?, `showAnswers` = ?,	`shuffleQuestions` = ?, `shuffleAnswers` = ?, ";
661			$query .= "`publishDate` = ?, `expireDate` = ?, ";
662			$query .= "`questionsPerPage` = ?, `timeLimited` = ?, `timeLimit` =?, `passingperct` = ?  where `quizId` = ?";
663			$bindvars = [$name,
664								$description,
665								$canRepeat,
666								$storeResults,
667								$immediateFeedback,
668								$showAnswers,
669								$shuffleQuestions,
670								$shuffleAnswers,
671								$publishDate,
672								$expireDate,
673								(int) $questionsPerPage,
674								$timeLimited,
675								(int) $timeLimit,
676								(int) $passingperct,
677								(int) $quizId
678			];
679
680			$result = $this->query($query, $bindvars);
681		} else {
682			// insert a new quiz
683
684			$query  = "insert into `tiki_quizzes`(`name`,`description`,`canRepeat`,`storeResults`,";
685			$query .= "`immediateFeedback`, `showAnswers`,	`shuffleQuestions`, `shuffleAnswers`,";
686			$query .= "`publishDate`, `expireDate`,";
687			$query .= "`questionsPerPage`,`timeLimited`,`timeLimit`,`created`,`taken`,`passingperct`)";
688			$query .= " values(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
689			$bindvars = [
690									$name,
691									$description,
692									$canRepeat,
693									$storeResults,
694									$immediateFeedback,
695									$showAnswers,
696									$shuffleQuestions,
697									$shuffleAnswers,
698									$publishDate,
699									$expireDate,
700									(int) $questionsPerPage,
701									$timeLimited,
702									(int) $timeLimit,
703									(int) $this->now,
704									0,
705									(int) $passingperct
706			];
707			$result = $this->query($query, $bindvars);
708			$queryid = "select max(`quizId`) from `tiki_quizzes` where `created`=?";
709			$quizId = $this->getOne($queryid, [(int) $this->now]);
710		}
711
712		return $quizId;
713	}
714
715	/**
716	 * @param $questionId
717	 * @param $question
718	 * @param $type
719	 * @param $quizId
720	 * @param $position
721	 * @return mixed
722	 */
723	public function replace_quiz_question($questionId, $question, $type, $quizId, $position)
724	{
725		if ($questionId) {
726			// update an existing quiz
727			$query = "update `tiki_quiz_questions` set `type`=?, `position` = ?, `question` = ?  where `questionId` = ? and `quizId`=?";
728			$bindvars = [$type,(int) $position, $question, (int) $questionId, (int) $quizId];
729			$result = $this->query($query, $bindvars);
730		} else {
731			// insert a new quiz
732
733			$query = "insert into `tiki_quiz_questions`(`question`,`type`,`quizId`,`position`) values(?,?,?,?)";
734			$bindvars = [$question, $type, (int) $quizId, (int) $position];
735			$result = $this->query($query, $bindvars);
736			$queryid = "select max(`questionId`) from `tiki_quiz_questions` where `question` like ? and `type`=?";
737			$questionId = $this->getOne($queryid, [substr($question, 0, 200) . "%", $type]);
738		}
739		return $questionId;
740	}
741
742	/**
743	 * @param $optionId
744	 * @param $option
745	 * @param $points
746	 * @param $questionId
747	 * @return mixed
748	 */
749	public function replace_question_option($optionId, $option, $points, $questionId)
750	{
751
752		// validating the points value
753		if ((! is_numeric($points)) || ($points == "")) {
754			$points = 0;
755		}
756		if ($optionId) {
757			$query = "update `tiki_quiz_question_options` set `points`=?, `optionText` = ?  where `optionId` = ? and `questionId`=?";
758			$bindvars = [(int) $points, $option,(int) $optionId, (int) $questionId];
759			$result = $this->query($query, $bindvars);
760		} else {
761			$query = "insert into `tiki_quiz_question_options`(`optionText`,`points`,`questionId`) values(?,?,?)";
762			$result = $this->query($query, [$option, (int) $points, (int) $questionId]);
763			$queryid = "select max(`optionId`) from `tiki_quiz_question_options` where `optionText`=? and `questionId`=?";
764			$optionId = $this->getOne($queryid, [$option, (int) $questionId]);
765		}
766
767		return $optionId;
768	}
769
770	/**
771	 * @param $questionId
772	 * @return bool
773	 */
774	public function get_quiz_question($questionId)
775	{
776		$query = "select * from `tiki_quiz_questions` where `questionId`=?";
777		$result = $this->query($query, [(int) $questionId]);
778		if (! $result->numRows()) {
779			return false;
780		}
781		$res = $result->fetchRow();
782		return $res;
783	}
784
785	/**
786	 * @param $optionId
787	 * @return bool
788	 */
789	public function get_quiz_question_option($optionId)
790	{
791		$query = "select * from `tiki_quiz_question_options` where `optionId`=?";
792		$result = $this->query($query, [(int) $optionId]);
793		if (! $result->numRows()) {
794			return false;
795		}
796		$res = $result->fetchRow();
797		return $res;
798	}
799
800	/**
801	 * @param $quizId
802	 * @param $offset
803	 * @param $maxRecords
804	 * @param $sort_mode
805	 * @param $find
806	 * @return array
807	 */
808	public function list_quiz_questions($quizId, $offset, $maxRecords, $sort_mode, $find)
809	{
810		if ($find) {
811			$findesc = '%' . $find . '%';
812			$mid = " where `quizId`=? and `question` like ? ";
813			$bindvars = [(int) $quizId, $findesc];
814		} else {
815			$mid = " where `quizId`=? ";
816			$bindvars = [(int) $quizId];
817		}
818
819		$query = "select * from `tiki_quiz_questions` $mid order by " . $this->convertSortMode($sort_mode);
820		$query_cant = "select count(*) from `tiki_quiz_questions` $mid";
821		$result = $this->query($query, $bindvars, $maxRecords, $offset);
822		$cant = $this->getOne($query_cant, $bindvars);
823		$ret = [];
824
825		while ($res = $result->fetchRow()) {
826			$res["options"] = $this->getOne("select count(*) from `tiki_quiz_question_options` where `questionId`=?", [(int) $res["questionId"]]);
827			$res["maxPoints"] = $this->getOne("select max(`points`) from `tiki_quiz_question_options` where `questionId`=?", [(int) $res["questionId"]]);
828			$ret[] = $res;
829		}
830
831		$retval = [];
832		$retval["data"] = $ret;
833		$retval["cant"] = $cant;
834		return $retval;
835	}
836
837	/**
838	 * @param $offset
839	 * @param $maxRecords
840	 * @param string $sort_mode
841	 * @param $find
842	 * @return array
843	 */
844	public function list_all_questions($offset, $maxRecords, $sort_mode = "position_desc", $find)
845	{
846		if ($find) {
847			$findesc = '%' . $find . '%';
848
849			$mid = " where `question` like ? ";
850			$bindvars = [$findesc];
851		} else {
852			$mid = " ";
853			$bindvars = [];
854		}
855
856		$query = "select * from `tiki_quiz_questions` $mid order by " . $this->convertSortMode($sort_mode);
857		$query_cant = "select count(*) from `tiki_quiz_questions` $mid";
858		$result = $this->query($query, $bindvars, $maxRecords, $offset);
859		$cant = $this->getOne($query_cant, $bindvars);
860		$ret = [];
861
862		while ($res = $result->fetchRow()) {
863			$res["options"]
864				= $this->getOne("select count(*) from `tiki_quiz_question_options` where `questionId`=?", [(int) $res["questionId"]]);
865
866			$ret[] = $res;
867		}
868
869		$retval = [];
870		$retval["data"] = $ret;
871		$retval["cant"] = $cant;
872		return $retval;
873	}
874
875	/**
876	 * @param $questionId
877	 * @param $offset
878	 * @param $maxRecords
879	 * @param $sort_mode
880	 * @param $find
881	 * @return array
882	 */
883	public function list_quiz_question_options($questionId, $offset, $maxRecords, $sort_mode, $find)
884	{
885		if ($find) {
886			$findesc = '%' . $find . '%';
887
888			$mid = " where `questionId`=? and `optionText` like ? ";
889			$bindvars = [(int) $questionId,$findesc];
890		} else {
891			$mid = " where `questionId`=? ";
892			$bindvars = [(int) $questionId];
893		}
894
895		$query = "select * from `tiki_quiz_question_options` $mid order by " . $this->convertSortMode($sort_mode);
896		$query_cant = "select count(*) from `tiki_quiz_question_options` $mid";
897		$result = $this->query($query, $bindvars, $maxRecords, $offset);
898		$cant = $this->getOne($query_cant, $bindvars);
899		$ret = [];
900
901		while ($res = $result->fetchRow()) {
902			$ret[] = $res;
903		}
904
905		$retval = [];
906		$retval["data"] = $ret;
907		$retval["cant"] = $cant;
908		return $retval;
909	}
910
911	/**
912	 * @param $questionId
913	 * @return bool
914	 */
915	public function remove_quiz_question($questionId)
916	{
917		$query = "delete from `tiki_quiz_questions` where `questionId`=?";
918
919		$result = $this->query($query, [(int) $questionId]);
920		// Remove all the options for the question
921		$query = "delete from `tiki_quiz_question_options` where `questionId`=?";
922		$result = $this->query($query, [(int) $questionId]);
923		return true;
924	}
925
926	/**
927	 * @param $optionId
928	 * @return bool
929	 */
930	public function remove_quiz_question_option($optionId)
931	{
932		$query = "delete from `tiki_quiz_question_options` where `optionId`=?";
933
934		$result = $this->query($query, [(int) $optionId]);
935		return true;
936	}
937
938	/**
939	 * @param $quizId
940	 * @return bool
941	 */
942	public function remove_quiz($quizId)
943	{
944		$query = "delete from `tiki_quizzes` where `quizId`=?";
945
946		$result = $this->query($query, [(int) $quizId]);
947		$query = "select * from `tiki_quiz_questions` where `quizId`=?";
948		$result = $this->query($query, [(int) $quizId]);
949
950		// Remove all the options for each question
951		while ($res = $result->fetchRow()) {
952			$questionId = $res["questionId"];
953
954			$query2 = "delete from `tiki_quiz_question_options` where `questionId`=?";
955			$result2 = $this->query($query2, [(int) $questionId]);
956		}
957
958		// Remove all the questions
959		$query = "delete from `tiki_quiz_questions` where `quizId`=?";
960		$result = $this->query($query, [(int) $quizId]);
961		$query = "delete from `tiki_quiz_results` where `quizId`=?";
962		$result = $this->query($query, [(int) $quizId]);
963		$query = "delete from `tiki_quiz_stats` where `quizId`=?";
964		$result = $this->query($query, [(int) $quizId]);
965		$query = "delete from `tiki_user_quizzes` where `quizId`=?";
966		$result = $this->query($query, [(int) $quizId]);
967		$query = "delete from `tiki_user_answers` where `quizId`=?";
968		$result = $this->query($query, [(int) $quizId]);
969		$this->remove_object('quiz', $quizId);
970		return true;
971	}
972
973	/**
974	 * @param $id
975	 * @return Quiz
976	 */
977	public function quiz_fetch($id)
978	{
979		if ($id == 0) {
980			$quiz = new Quiz;
981		} else {
982			echo __FILE__ . " line: " . __LINE__ . " : Need to fetch a quiz from the database" . "<br />";
983		}
984		return $quiz;
985	}
986
987	// $quiz is a quiz object
988	/**
989	 * @param $quiz
990	 * @return mixed
991	 */
992	public function quiz_store($quiz)
993	{
994		echo __FILE__ . " line: " . __LINE__ . ": in quizlib->quiz_store<br />";
995		echo "Store stuff in the dbFields array.<br />";
996		foreach ($quiz->dbFields as $f) {
997		}
998		die;
999		if ($quizId) {
1000			// update an existing quiz
1001			$query = "update `tiki_quizzes` set `name` = ?, `description` = ?, `canRepeat` = ?, `storeResults` = ?,";
1002			$query .= "`immediateFeedback` = ?, `showAnswers` = ?,	`shuffleQuestions` = ?, `shuffleAnswers` = ?, ";
1003			$query .= "`publishDate` = ?, `expireDate` = ?, ";
1004			$query .= "`questionsPerPage` = ?, `timeLimited` = ?, `timeLimit` =?  where `quizId` = ?";
1005			$bindvars = [
1006							$name,
1007							$description,
1008							$canRepeat,
1009							$storeResults,
1010							$immediateFeedback,
1011							$showAnswers,
1012							$shuffleQuestions,
1013							$shuffleAnswers,
1014							$publishDate,
1015							$expireDate,
1016							(int) $questionsPerPage,
1017							$timeLimited,
1018							(int) $timeLimit,
1019							(int) $quizId
1020			];
1021
1022			$result = $this->query($query, $bindvars);
1023		} else {
1024			// insert a new quiz
1025
1026			$query = "insert into `tiki_quizzes`(`name`,`description`,`canRepeat`,`storeResults`,";
1027			$query .= "`immediateFeedback`, `showAnswers`,	`shuffleQuestions`, `shuffleAnswers`,";
1028			$query .= "`publishDate`, `expireDate`,";
1029			$query .= "`questionsPerPage`,`timeLimited`,`timeLimit`,`created`,`taken`) values(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
1030			$bindvars = [$name,
1031								$description,
1032								$canRepeat,
1033								$storeResults,
1034								$immediateFeedback,
1035								$showAnswers,
1036								$shuffleQuestions,
1037								$shuffleAnswers,
1038								$publishDate,
1039								$expireDate,
1040								(int) $questionsPerPage,
1041								$timeLimited,
1042								(int) $timeLimit,
1043								(int) $this->now,
1044								0
1045			];
1046			$result = $this->query($query, $bindvars);
1047			$queryid = "select max(`quizId`) from `tiki_quizzes` where `created`=?";
1048			$quizId = $this->getOne($queryid, [(int) $this->now]);
1049		}
1050
1051		return $quizId;
1052	}
1053
1054	/**
1055	 * @return string
1056	 */
1057	public function get_upload_dir()
1058	{
1059		return "quiz_uploads/";
1060	}
1061}
1062
1063// Find the next non-blank or return -1
1064/**
1065 * @param $text
1066 * @return int
1067 */
1068function NextText($text)
1069{
1070	$found = -1;
1071	for ($i = 0, $icount_text = count($text); $i < $icount_text; $i++) {
1072		if (strlen($text[$i]) > 0) {
1073			$found = $i;
1074			break;
1075		}
1076	}
1077	return $found;
1078}
1079
1080// Find the next blank or retrun the last element
1081/**
1082 * @param $text
1083 * @return int
1084 */
1085function NextBlank($text)
1086{
1087	$found = 0;
1088	for ($i = 0, $icount_text = count($text); $i < $icount_text; $i++) {
1089		$found = $i;
1090		if ($text[$i] == "") {
1091			break;
1092		}
1093	}
1094	return $found;
1095}
1096
1097/**
1098 * @param $s
1099 */
1100function quizlib_error_exit($s)
1101{
1102	$smarty = TikiLib::lib('smarty');
1103	$smarty->assign('msg', $s);
1104
1105	$smarty->display("error.tpl");
1106	die;
1107}
1108
1109// Called by tiki-edit_quiz_questions.php
1110// Convert a block of text into an array of question objects.
1111/**
1112 * @param $text
1113 * @return array
1114 */
1115function TextToQuestions($text)
1116{
1117	$text = preg_replace("/\r\n/", "\n", $text);
1118	$text = preg_replace("/\n\r/", "\n", $text);
1119	$text = preg_replace("/\r/", "\n", $text);
1120	$text = preg_replace("/\t/", " ", $text);
1121	$text = preg_replace("/[ ]+/", " ", $text);
1122
1123	$text = preg_split("/\n/", $text);
1124
1125	if ($text[count($text) - 1] != "") {
1126		$text[] = "";
1127	}
1128
1129	for ($i = 0, $icount_text = count($text); $i < $icount_text; $i++) {
1130		$text[$i] = trim($text[$i]);
1131		if ($text[$i] and ! ctype_print($text[$i])) {
1132			quizlib_error_exit(
1133				"lib/quizzes/quizlib.php line "
1134				. __LINE__
1135				. ": Your text has invalid character(s) near line $i where it says:\n  $text[$i]"
1136			);
1137		}
1138	}
1139
1140	$questions = [];
1141
1142	while (NextText($text) != -1) {
1143		$text = array_slice($text, NextText($text));
1144		$lines = array_slice($text, 0, NextBlank($text));
1145		$text = array_slice($text, NextBlank($text));
1146		if (count($lines) > 0) {
1147			$question = new HW_QuizQuestionMultipleChoice($lines);
1148			array_push($questions, $question);
1149		}
1150	}
1151	return $questions;
1152}
1153
1154// An abstract class
1155/**
1156 *
1157 */
1158class HW_QuizQuestion
1159{
1160	public $question;
1161
1162	/**
1163	 * @param $lines
1164	 */
1165	public function from_text($lines)
1166	{
1167		// Set the question according to an array of text lines.
1168	}
1169	public function getQuestion()
1170	{
1171		return $this->question;
1172	}
1173	public function to_text()
1174	{
1175		// Export the question to an array of text lines.
1176	}
1177	public function getAnswerCount()
1178	{
1179		// How many possible answers (i.e. choices in a multiple-choice)
1180	}
1181}
1182
1183// A multiple-choice quiz question
1184// e.g.
1185//   $question = "What is your favorite color?";
1186//   $choices = Array(Array('text'=>"Red",  'correct'=>1),
1187//                    Array('text'=>"Blue", 'correct'=>1),
1188//                    Array('text'=>"Green",'correct'=>1));
1189//   Any of the answers are correct in this example.
1190/**
1191 *
1192 */
1193class HW_QuizQuestionMultipleChoice extends HW_QuizQuestion
1194{
1195	public $choices  = [];
1196
1197	/**
1198	 * @param $lines
1199	 */
1200	public function __construct($lines)
1201	{
1202		$this->from_text($lines);
1203	}
1204
1205	// Import from text array
1206	// $lines is in array of text items.
1207	//   The 0th line is the question.
1208	//   The rest of the lines are answers.
1209	//   Correct answers start with a "*"
1210	/**
1211	 * @param $lines
1212	 */
1213	public function from_text($lines)
1214	{
1215		$this->question = $lines[0];
1216		$this->choices  = [];
1217		$lines = array_slice($lines, 1);
1218		foreach ($lines as $line) {
1219			if (preg_match("/^\*\s*(.*)/", $line, $match)) {
1220				// Ignore spaces after the "*"
1221				$a = ['text' => $match[1],'correct' => 1];
1222			} else {
1223				$a = ['text' => $line,'correct' => 0];
1224			}
1225			array_push($this->choices, $a);
1226		}
1227	}
1228
1229	// Export the question to an array of text lines.
1230	/**
1231	 * @param bool $show_answer
1232	 * @return array
1233	 */
1234	public function to_text($show_answer = false)
1235	{
1236		$lines = [];
1237		array_push($lines, $this->question);
1238		foreach ($this->choices as $choice) {
1239			if ($show_answer && $choice['correct']) {
1240				array_push($lines, "*" . $choice['text']);
1241			} else {
1242				array_push($lines, " " . $choice['text']);
1243			}
1244		}
1245		return $lines;
1246	}
1247
1248	/**
1249	 * @return int
1250	 */
1251	public function getChoiceCount()
1252	{
1253		return count($this->choices);
1254	}
1255
1256	/**
1257	 * @param $i
1258	 * @return mixed
1259	 */
1260	public function getChoice($i)
1261	{
1262		return $this->choices[$i]['text'];
1263	}
1264
1265	/**
1266	 * @param $i
1267	 * @return mixed
1268	 */
1269	public function getCorrect($i)
1270	{
1271		return $this->choices[$i]['correct'];
1272	}
1273
1274	public function dump()
1275	{
1276		echo "question = \"" . $this->question . "\"\n";
1277		echo "choices =\n";
1278		foreach ($this->choices as $choice) {
1279			if ($choice['correct']) {
1280				echo "*";
1281			} else {
1282				echo " ";
1283			}
1284			echo $choice['text'] . "\n";
1285		}
1286	}
1287}
1288
1289// A Yes-No quiz question
1290// e.g.
1291//   $question = "Do you wiki?";
1292//   $answer   = -1 (unknown), 0 (no), 1 (yes)
1293/**
1294 *
1295 */
1296class HW_QuizQuestionYesNo extends HW_QuizQuestion
1297{
1298	public $question;
1299	public $answer  = -1;
1300
1301	/**
1302	 * @param $lines
1303	 */
1304	public function __construct($lines)
1305	{
1306		$this->from_text($lines);
1307	}
1308
1309	// Import from text array
1310	// $lines is in array of text items.
1311	//   The 0th line is the question.
1312	//   The first line is the answer.
1313	/**
1314	 * @param $lines
1315	 */
1316	public function from_text($lines)
1317	{
1318		$this->question = $lines[0];
1319		if (preg_match("/^\s*[Yy][Ee][Ss]\s*$/", $lines[1])) {
1320			// Ignore spaces and case
1321			$this->answer = 1;
1322		} elseif (preg_match("/^\s*[Nn][Oo]\s*$/", $lines[1])) {
1323			// Ignore spaces and case
1324			$this->answer = 0;
1325		} else {
1326			$this->answer = -1;
1327		}
1328	}
1329
1330	/**
1331	 * @param bool $show_answer
1332	 * @return array
1333	 */
1334	public function to_text($show_answer = false)
1335	{
1336		// Export the question to an array of text lines.
1337		$lines = [];
1338		array_push($lines, $this->question);
1339		if ($this->answer == 1) {
1340			array_push($lines, " Yes");
1341		} elseif ($this->answer == 0) {
1342			array_push($lines, " No");
1343		} else {
1344			array_push($lines, " Unknown");
1345		}
1346		return $lines;
1347	}
1348
1349	public function dump()
1350	{
1351		echo "question = \"" . $this->question . "\"\n";
1352		echo "answer = $this->answer\n";
1353	}
1354}
1355
1356/**
1357 *
1358 */
1359class Quiz
1360{
1361	public $id;
1362	public $bDeleted;
1363	public $timestamp;
1364	public $nAuthor;
1365	public $bOnline;
1366	public $nTaken;
1367	public $sName;
1368	public $sDescription;
1369	public $datePub;
1370	public $dateExp;
1371	public $bRandomQuestions;
1372	public $nRandomQuestions;
1373	public $bShuffleQuestions;
1374	public $bShuffleAnswers;
1375	public $bLimitQuestionsPerPage;
1376	public $nLimitQuestionsPerPage;
1377	public $bTimeLimited;
1378	public $nTimeLimit;
1379	public $bMultiSession;
1380	public $bCanRepeat;
1381	public $nCanRepeat;
1382	public $sGradingMethod;
1383	public $sShowScore;
1384	public $sShowCorrectAnswers;
1385	public $sPublishStats;
1386	public $bAdditionalQuestions;
1387	public $bForum;
1388	public $sForum;
1389	public $sPrologue;
1390	public $sData;
1391	public $sEpilogue;
1392	public $dbFields;
1393
1394	public function __construct()
1395	{
1396		global $user;
1397		$userlib = TikiLib::lib('user');
1398		$this->dbFields = [
1399				"id",
1400				"bDeleted",
1401				"timestamp",
1402				"nAuthor",
1403				"bOnline",
1404				"nTaken",
1405				"sName",
1406				"sDescription",
1407				"datePub",
1408				"dateExp",
1409				"bRandomQuestions",
1410				"nRandomQuestions",
1411				"bShuffleQuestions",
1412				"bShuffleAnswers",
1413				"bLimitQuestionsPerPage",
1414				"nLimitQuestionsPerPage",
1415				"bTimeLimited",
1416				"nTimeLimit",
1417				"bMultiSession",
1418				"bCanRepeat",
1419				"nCanRepeat",
1420				"sGradingMethod",
1421				"sShowScore",
1422				"sShowCorrectAnswers",
1423				"sPublishStats",
1424				"bAdditionalQuestions",
1425				"bForum",
1426				"sForum",
1427				"sPrologue",
1428				"sData",
1429				"sEpilogue"
1430		];
1431		$this->id = 0;
1432		$this->bDeleted = 0;
1433		$this->timestamp = $this->now;
1434		$this->nAuthor = $userlib->get_user_id($user);
1435		$this->sAuthor = $user;
1436		$this->bOnline = 'n';
1437		$this->nTaken = 'n';
1438		$this->sName = "";
1439		$this->sDescription = "";
1440		$this->datePub = $this->now;
1441		$this->dateExp = TikiLib::make_time(0, 0, 0, 1, 1, TikiLib::date_format("%Y") + 10);
1442		$this->bRandomQuestions = "y";
1443		$this->nRandomQuestions = 10;
1444		$this->nShuffleQuestions = "y";
1445		$this->bShuffleAnswers = "y";
1446		$this->bLimitQuestionsPerPage = "y";
1447		$this->nLimitQuestionsPerPage = 1;
1448		$this->bTimeLimited = "n";
1449		$this->nTimeLimit = "1";
1450		$this->bMultiSession = "n";
1451		$this->bCanRepeat = "y";
1452		$this->nCanRepeat = "unlimited";
1453		$this->sGradingMethod = "machine";
1454		$this->sShowScore = "immediately";
1455		$this->sShowCorrectAnswers = "immediately";
1456		$this->sPublishStats = "immediately";
1457		$this->bAdditionalQuestions = "n";
1458
1459		$this->forum = "n";
1460		$this->forumName = "";
1461		$this->prologue = "";
1462		$this->epilogue = "";
1463	}
1464
1465	// dump as html text
1466	/**
1467	 * @return array
1468	 */
1469	public function show_html()
1470	{
1471		$userlib = TikiLib::lib('user');
1472		$lines = [];
1473		$lines[] = "id = " . $this->id . "<br />";
1474		$lines[] = "deleted = " . $this->deleted . "<br />";
1475		$authorInfo = $userlib->get_userid_info($this->author);
1476		$lines[] = "author id = " . $this->author . "; author login = " . $authorInfo["login"] . "<br />";
1477		$lines[] = "version = " . $this->version . "<br />";
1478		$lines[] = "timestamp = " . $this->date_format("%a, %e %b %Y %H:%M:%S %O", $this->timestamp) . "<br />";
1479		$lines[] = "online = " . $this->online . "<br />";
1480		$lines[] = "studentAttempts = " . $this->studentAttempts . "<br />";
1481		$lines[] = "name = " . $this->name . "<br />";
1482		$lines[] = "description = " . $this->description . "<br />";
1483		$lines[] = "datePub = " . $this->date_format("%a, %e %b %Y %H:%M:%S %O", $this->datePub) . "<br />";
1484		$lines[] = "dateExp = " . $this->date_format("%a, %e %b %Y %H:%M:%S %O", $this->dateExp) . "<br />";
1485		$lines[] = "nQuestion = " . $this->nQuestion . "<br />";
1486		$lines[] = "nQuestions = " . $this->nQuestions . "<br />";
1487		$lines[] = "shuffleQuestions = " . $this->shuffleQuestions . "<br />";
1488		$lines[] = "shuffleAnswers = " . $this->shuffleAnswers . "<br />";
1489		$lines[] = "limitDisplay = " . $this->limitDisplay . "<br />";
1490		$lines[] = "questionsPerPage = " . $this->questionsPerPage . "<br />";
1491		$lines[] = "timeLimited = " . $this->timeLimited . "<br />";
1492		$lines[] = "timeLimit = " . $this->timeLimit . "<br />";
1493		$lines[] = "multiSession = " . $this->multiSession . "<br />";
1494		$lines[] = "canRepeat = " . $this->canRepeat . "<br />";
1495		$lines[] = "repetitions = " . $this->repetitions . "<br />";
1496		$lines[] = "gradingMethod = " . $this->gradingMethod . "<br />";
1497		$lines[] = "showScore = " . $this->showScore . "<br />";
1498		$lines[] = "showCorrectAnswers = " . $this->showCorrectAnswers . "<br />";
1499		$lines[] = "publishStats = " . $this->publishStats . "<br />";
1500		$lines[] = "additionalQuestions = " . $this->additionalQuestions . "<br />";
1501		$lines[] = "forum = " . $this->forum . "<br />";
1502		$lines[] = "forumName = " . $this->forumName . "<br />";
1503		$lines[] = "data = " . $this->data . "<br />";
1504		return $lines;
1505	}
1506
1507	// Use any data in the array to replace the instance data.
1508	/**
1509	 * @param $data
1510	 */
1511	public function data_load($data)
1512	{
1513		foreach ($this as $key => $val) {
1514			if (isset($data[$key]) && ($data[$key] != $val)) {
1515				$this->$key = $data[$key];
1516			}
1517		}
1518	}
1519
1520	/**
1521	 * @param $quiz
1522	 */
1523	public function compare($quiz)
1524	{
1525	}
1526
1527	public function getAnswerCount()
1528	{
1529		// How many possible answers (i.e. choices in a multiple-choice)
1530	}
1531}
1532