question.class.php 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875
  1. <?php
  2. /*
  3. DOKEOS - elearning and course management software
  4. For a full list of contributors, see documentation/credits.html
  5. This program is free software; you can redistribute it and/or
  6. modify it under the terms of the GNU General Public License
  7. as published by the Free Software Foundation; either version 2
  8. of the License, or (at your option) any later version.
  9. See "documentation/licence.html" more details.
  10. Contact:
  11. Dokeos
  12. Rue des Palais 44 Paleizenstraat
  13. B-1030 Brussels - Belgium
  14. Tel. +32 (2) 211 34 56
  15. */
  16. /**
  17. * File containing the Question class.
  18. * @package dokeos.exercise
  19. * @author Olivier Brouckaert
  20. * @version $Id: question.class.php 14272 2008-02-08 13:02:50Z elixir_inter $
  21. */
  22. if(!class_exists('Question')):
  23. // answer types
  24. define(UNIQUE_ANSWER, 1);
  25. define(MULTIPLE_ANSWER, 2);
  26. define(FILL_IN_BLANKS, 3);
  27. define(MATCHING, 4);
  28. define(FREE_ANSWER, 5);
  29. define(HOT_SPOT, 6);
  30. define(HOT_SPOT_ORDER, 7);
  31. /**
  32. CLASS QUESTION
  33. *
  34. * This class allows to instantiate an object of type Question
  35. *
  36. * @author Olivier Brouckaert, original author
  37. * @author Patrick Cool, LaTeX support
  38. * @package dokeos.exercise
  39. */
  40. abstract class Question
  41. {
  42. var $id;
  43. var $question;
  44. var $description;
  45. var $weighting;
  46. var $position;
  47. var $type;
  48. var $picture;
  49. var $exerciseList; // array with the list of exercises which this question is in
  50. static $typePicture = 'new_question.png';
  51. static $explanationLangVar = '';
  52. static $questionTypes = array(
  53. UNIQUE_ANSWER => array('unique_answer.class.php' , 'UniqueAnswer'),
  54. MULTIPLE_ANSWER => array('multiple_answer.class.php' , 'MultipleAnswer'),
  55. FILL_IN_BLANKS => array('fill_blanks.class.php' , 'FillBlanks'),
  56. MATCHING => array('matching.class.php' , 'Matching'),
  57. FREE_ANSWER => array('freeanswer.class.php' , 'FreeAnswer'),
  58. HOT_SPOT => array('hotspot.class.php' , 'HotSpot')
  59. );
  60. /**
  61. * constructor of the class
  62. *
  63. * @author - Olivier Brouckaert
  64. */
  65. function Question()
  66. {
  67. $this->id=0;
  68. $this->question='';
  69. $this->description='';
  70. $this->weighting=0;
  71. $this->position=1;
  72. $this->picture='';
  73. $this->exerciseList=array();
  74. }
  75. /**
  76. * reads question informations from the data base
  77. *
  78. * @author - Olivier Brouckaert
  79. * @param - integer $id - question ID
  80. * @return - boolean - true if question exists, otherwise false
  81. */
  82. static function read($id)
  83. {
  84. global $_course;
  85. $TBL_EXERCICES = Database::get_course_table(TABLE_QUIZ_TEST);
  86. $TBL_QUESTIONS = Database::get_course_table(TABLE_QUIZ_QUESTION);
  87. $TBL_EXERCICE_QUESTION = Database::get_course_table(TABLE_QUIZ_TEST_QUESTION);
  88. $sql="SELECT question,description,ponderation,position,type,picture FROM $TBL_QUESTIONS WHERE id='$id'";
  89. $result=api_sql_query($sql,__FILE__,__LINE__);
  90. // if the question has been found
  91. if($object=mysql_fetch_object($result))
  92. {
  93. $objQuestion = Question::getInstance($object->type);
  94. $objQuestion->id=$id;
  95. $objQuestion->question=$object->question;
  96. $objQuestion->description=$object->description;
  97. $objQuestion->weighting=$object->ponderation;
  98. $objQuestion->position=$object->position;
  99. $objQuestion->type=$object->type;
  100. $objQuestion->picture=$object->picture;
  101. $sql="SELECT exercice_id FROM $TBL_EXERCICE_QUESTION WHERE question_id='".intval($id)."'";
  102. $result=api_sql_query($sql,__FILE__,__LINE__);
  103. // fills the array with the exercises which this question is in
  104. while($object=mysql_fetch_object($result))
  105. {
  106. $objQuestion->exerciseList[]=$object->exercice_id;
  107. }
  108. return $objQuestion;
  109. }
  110. // question not found
  111. return false;
  112. }
  113. /**
  114. * returns the question ID
  115. *
  116. * @author - Olivier Brouckaert
  117. * @return - integer - question ID
  118. */
  119. function selectId()
  120. {
  121. return $this->id;
  122. }
  123. /**
  124. * returns the question title
  125. *
  126. * @author - Olivier Brouckaert
  127. * @return - string - question title
  128. */
  129. function selectTitle()
  130. {
  131. $this->question=api_parse_tex($this->question);
  132. return $this->question;
  133. }
  134. /**
  135. * returns the question description
  136. *
  137. * @author - Olivier Brouckaert
  138. * @return - string - question description
  139. */
  140. function selectDescription()
  141. {
  142. $this->description=api_parse_tex($this->description);
  143. return $this->description;
  144. }
  145. /**
  146. * returns the question weighting
  147. *
  148. * @author - Olivier Brouckaert
  149. * @return - integer - question weighting
  150. */
  151. function selectWeighting()
  152. {
  153. return $this->weighting;
  154. }
  155. /**
  156. * returns the question position
  157. *
  158. * @author - Olivier Brouckaert
  159. * @return - integer - question position
  160. */
  161. function selectPosition()
  162. {
  163. return $this->position;
  164. }
  165. /**
  166. * returns the answer type
  167. *
  168. * @author - Olivier Brouckaert
  169. * @return - integer - answer type
  170. */
  171. function selectType()
  172. {
  173. return $this->type;
  174. }
  175. /**
  176. * returns the picture name
  177. *
  178. * @author - Olivier Brouckaert
  179. * @return - string - picture name
  180. */
  181. function selectPicture()
  182. {
  183. return $this->picture;
  184. }
  185. /**
  186. * returns the array with the exercise ID list
  187. *
  188. * @author - Olivier Brouckaert
  189. * @return - array - list of exercise ID which the question is in
  190. */
  191. function selectExerciseList()
  192. {
  193. return $this->exerciseList;
  194. }
  195. /**
  196. * returns the number of exercises which this question is in
  197. *
  198. * @author - Olivier Brouckaert
  199. * @return - integer - number of exercises
  200. */
  201. function selectNbrExercises()
  202. {
  203. return sizeof($this->exerciseList);
  204. }
  205. /**
  206. * changes the question title
  207. *
  208. * @author - Olivier Brouckaert
  209. * @param - string $title - question title
  210. */
  211. function updateTitle($title)
  212. {
  213. $this->question=$title;
  214. }
  215. /**
  216. * changes the question description
  217. *
  218. * @author - Olivier Brouckaert
  219. * @param - string $description - question description
  220. */
  221. function updateDescription($description)
  222. {
  223. $this->description=$description;
  224. }
  225. /**
  226. * changes the question weighting
  227. *
  228. * @author - Olivier Brouckaert
  229. * @param - integer $weighting - question weighting
  230. */
  231. function updateWeighting($weighting)
  232. {
  233. $this->weighting=$weighting;
  234. }
  235. /**
  236. * changes the question position
  237. *
  238. * @author - Olivier Brouckaert
  239. * @param - integer $position - question position
  240. */
  241. function updatePosition($position)
  242. {
  243. $this->position=$position;
  244. }
  245. /**
  246. * changes the answer type. If the user changes the type from "unique answer" to "multiple answers"
  247. * (or conversely) answers are not deleted, otherwise yes
  248. *
  249. * @author - Olivier Brouckaert
  250. * @param - integer $type - answer type
  251. */
  252. function updateType($type)
  253. {
  254. global $TBL_REPONSES;
  255. // if we really change the type
  256. if($type != $this->type)
  257. {
  258. // if we don't change from "unique answer" to "multiple answers" (or conversely)
  259. if(!in_array($this->type,array(UNIQUE_ANSWER,MULTIPLE_ANSWER)) || !in_array($type,array(UNIQUE_ANSWER,MULTIPLE_ANSWER)))
  260. {
  261. // removes old answers
  262. $sql="DELETE FROM $TBL_REPONSES WHERE question_id='".$this->id."'";
  263. api_sql_query($sql,__FILE__,__LINE__);
  264. }
  265. $this->type=$type;
  266. }
  267. }
  268. /**
  269. * adds a picture to the question
  270. *
  271. * @author - Olivier Brouckaert
  272. * @param - string $Picture - temporary path of the picture to upload
  273. * @param - string $PictureName - Name of the picture
  274. * @return - boolean - true if uploaded, otherwise false
  275. */
  276. function uploadPicture($Picture,$PictureName)
  277. {
  278. global $picturePath, $_course, $_user;
  279. // if the question has got an ID
  280. if($this->id)
  281. {
  282. $extension = pathinfo($PictureName, PATHINFO_EXTENSION);
  283. $this->picture='quiz-'.$this->id.'.jpg';
  284. if($extension == 'gif' || $extension == 'png')
  285. {
  286. $o_img = new image($Picture);
  287. $o_img->send_image('JPG',$picturePath.'/'.$this->picture);
  288. $document_id = add_document($_course, '/images/'.$this->picture, 'file', filesize($picturePath.'/'.$this->picture),$this->picture);
  289. }
  290. else
  291. {
  292. move_uploaded_file($Picture,$picturePath.'/'.$this->picture)?true:false;
  293. }
  294. $document_id = add_document($_course, '/images/'.$this->picture, 'file', filesize($picturePath.'/'.$this->picture),$this->picture);
  295. if($document_id)
  296. {
  297. return api_item_property_update($_course, TOOL_DOCUMENT, $document_id, 'DocumentAdded', $_user['user_id']);
  298. }
  299. }
  300. return false;
  301. }
  302. /**
  303. * Resizes a picture || Warning!: can only be called after uploadPicture, or if picture is already available in object.
  304. *
  305. * @author - Toon Keppens
  306. * @param - string $Dimension - Resizing happens proportional according to given dimension: height|width|any
  307. * @param - integer $Max - Maximum size
  308. * @return - boolean - true if success, false if failed
  309. */
  310. function resizePicture($Dimension, $Max)
  311. {
  312. global $picturePath;
  313. // if the question has an ID
  314. if($this->id)
  315. {
  316. // Get dimensions from current image.
  317. $current_img = imagecreatefromjpeg($picturePath.'/'.$this->picture);
  318. $current_image_size = getimagesize($picturePath.'/'.$this->picture);
  319. $current_height = imagesy($current_img);
  320. $current_width = imagesx($current_img);
  321. if($current_image_size[0] < $Max && $current_image_size[1] <$Max)
  322. return true;
  323. elseif($current_height == "")
  324. return false;
  325. // Resize according to height.
  326. if ($Dimension == "height")
  327. {
  328. $resize_scale = $current_height / $Max;
  329. $new_height = $Max;
  330. $new_width = ceil($current_width / $resize_scale);
  331. }
  332. // Resize according to width
  333. if ($Dimension == "width")
  334. {
  335. $resize_scale = $current_width / $Max;
  336. $new_width = $Max;
  337. $new_height = ceil($current_height / $resize_scale);
  338. }
  339. // Resize according to height or width, both should not be larger than $Max after resizing.
  340. if ($Dimension == "any")
  341. {
  342. if ($current_height > $current_width || $current_height == $current_width)
  343. {
  344. $resize_scale = $current_height / $Max;
  345. $new_height = $Max;
  346. $new_width = ceil($current_width / $resize_scale);
  347. }
  348. if ($current_height < $current_width)
  349. {
  350. $resize_scale = $current_width / $Max;
  351. $new_width = $Max;
  352. $new_height = ceil($current_height / $resize_scale);
  353. }
  354. }
  355. // Create new image
  356. $new_img = imagecreatetruecolor($new_width, $new_height);
  357. $bgColor = imagecolorallocate($new_img, 255,255,255);
  358. imagefill($new_img , 0,0 , $bgColor);
  359. // Resize image
  360. imagecopyresized($new_img, $current_img, 0, 0, 0, 0, $new_width, $new_height, $current_width, $current_height);
  361. // Write image to file
  362. $result = imagejpeg($new_img, $picturePath.'/'.$this->picture, 100);
  363. // Delete temperory images, clear memory
  364. imagedestroy($current_img);
  365. imagedestroy($new_img);
  366. if ($result)
  367. {
  368. return true;
  369. }
  370. else
  371. {
  372. return false;
  373. }
  374. }
  375. }
  376. /**
  377. * deletes the picture
  378. *
  379. * @author - Olivier Brouckaert
  380. * @return - boolean - true if removed, otherwise false
  381. */
  382. function removePicture()
  383. {
  384. global $picturePath;
  385. // if the question has got an ID and if the picture exists
  386. if($this->id)
  387. {
  388. $picture=$this->picture;
  389. $this->picture='';
  390. return @unlink($picturePath.'/'.$picture)?true:false;
  391. }
  392. return false;
  393. }
  394. /**
  395. * exports a picture to another question
  396. *
  397. * @author - Olivier Brouckaert
  398. * @param - integer $questionId - ID of the target question
  399. * @return - boolean - true if copied, otherwise false
  400. */
  401. function exportPicture($questionId)
  402. {
  403. global $TBL_QUESTIONS, $picturePath;
  404. // if the question has got an ID and if the picture exists
  405. if($this->id && !empty($this->picture))
  406. {
  407. $picture=explode('.',$this->picture);
  408. $Extension=$picture[sizeof($picture)-1];
  409. $picture='quiz-'.$questionId.'.'.$Extension;
  410. $sql="UPDATE $TBL_QUESTIONS SET picture='$picture' WHERE id='$questionId'";
  411. api_sql_query($sql,__FILE__,__LINE__);
  412. return @copy($picturePath.'/'.$this->picture,$picturePath.'/'.$picture)?true:false;
  413. }
  414. return false;
  415. }
  416. /**
  417. * saves the picture coming from POST into a temporary file
  418. * Temporary pictures are used when we don't want to save a picture right after a form submission.
  419. * For example, if we first show a confirmation box.
  420. *
  421. * @author - Olivier Brouckaert
  422. * @param - string $Picture - temporary path of the picture to move
  423. * @param - string $PictureName - Name of the picture
  424. */
  425. function setTmpPicture($Picture,$PictureName)
  426. {
  427. global $picturePath;
  428. $PictureName=explode('.',$PictureName);
  429. $Extension=$PictureName[sizeof($PictureName)-1];
  430. // saves the picture into a temporary file
  431. @move_uploaded_file($Picture,$picturePath.'/tmp.'.$Extension);
  432. }
  433. /**
  434. * moves the temporary question "tmp" to "quiz-$questionId"
  435. * Temporary pictures are used when we don't want to save a picture right after a form submission.
  436. * For example, if we first show a confirmation box.
  437. *
  438. * @author - Olivier Brouckaert
  439. * @return - boolean - true if moved, otherwise false
  440. */
  441. function getTmpPicture()
  442. {
  443. global $picturePath;
  444. // if the question has got an ID and if the picture exists
  445. if($this->id)
  446. {
  447. if(file_exists($picturePath.'/tmp.jpg'))
  448. {
  449. $Extension='jpg';
  450. }
  451. elseif(file_exists($picturePath.'/tmp.gif'))
  452. {
  453. $Extension='gif';
  454. }
  455. elseif(file_exists($picturePath.'/tmp.png'))
  456. {
  457. $Extension='png';
  458. }
  459. $this->picture='quiz-'.$this->id.'.'.$Extension;
  460. return @rename($picturePath.'/tmp.'.$Extension,$picturePath.'/'.$this->picture)?true:false;
  461. }
  462. return false;
  463. }
  464. /**
  465. * updates the question in the data base
  466. * if an exercise ID is provided, we add that exercise ID into the exercise list
  467. *
  468. * @author - Olivier Brouckaert
  469. * @param - integer $exerciseId - exercise ID if saving in an exercise
  470. */
  471. function save($exerciseId=0)
  472. {
  473. global $TBL_QUESTIONS, $TBL_EXERCICE_QUESTION, $_course;
  474. $id=$this->id;
  475. $question=addslashes($this->question);
  476. $description=addslashes($this->description);
  477. $weighting=$this->weighting;
  478. $position=$this->position;
  479. $type=$this->type;
  480. $picture=addslashes($this->picture);
  481. // question already exists
  482. if($id)
  483. {
  484. $sql="UPDATE $TBL_QUESTIONS SET question='$question',description='$description',ponderation='$weighting',position='$position',type='$type',picture='$picture' WHERE id='$id'";
  485. api_sql_query($sql,__FILE__,__LINE__);
  486. }
  487. // creates a new question
  488. else
  489. {
  490. $sql="SELECT max(position) FROM $TBL_QUESTIONS as question, $TBL_EXERCICE_QUESTION as test_question WHERE question.id=test_question.question_id AND test_question.exercice_id='$exerciseId'";
  491. $result=api_sql_query($sql);
  492. $current_position=mysql_result($result,0,0);
  493. $this -> updatePosition($current_position+1);
  494. $position = $this -> position;
  495. $sql="INSERT INTO $TBL_QUESTIONS(question,description,ponderation,position,type,picture) VALUES('$question','$description','$weighting','$position','$type','$picture')";
  496. api_sql_query($sql,__FILE__,__LINE__);
  497. $this->id=mysql_insert_id();
  498. // If hotspot, create first answer
  499. if ($type == HOT_SPOT || $type == HOT_SPOT_ORDER) {
  500. $TBL_ANSWERS = Database::get_course_table(TABLE_QUIZ_ANSWER);
  501. $sql="INSERT INTO $TBL_ANSWERS (`id` , `question_id` , `answer` , `correct` , `comment` , `ponderation` , `position` , `hotspot_coordinates` , `hotspot_type` ) VALUES ('1', '$this->id', '', NULL , '', NULL , '1', '0;0|0|0', 'square')";
  502. api_sql_query($sql,__FILE__,__LINE__);
  503. }
  504. }
  505. // if the question is created in an exercise
  506. if($exerciseId)
  507. {
  508. $sql = 'UPDATE '.Database::get_course_table(TABLE_LP_ITEM).'
  509. SET max_score = '.intval($weighting).'
  510. WHERE item_type = "'.TOOL_QUIZ.'"
  511. AND path='.intval($exerciseId);
  512. api_sql_query($sql,__FILE__,__LINE__);
  513. // adds the exercise into the exercise list of this question
  514. $this->addToList($exerciseId);
  515. }
  516. }
  517. /**
  518. * adds an exercise into the exercise list
  519. *
  520. * @author - Olivier Brouckaert
  521. * @param - integer $exerciseId - exercise ID
  522. */
  523. function addToList($exerciseId)
  524. {
  525. global $TBL_EXERCICE_QUESTION;
  526. $id=$this->id;
  527. // checks if the exercise ID is not in the list
  528. if(!in_array($exerciseId,$this->exerciseList))
  529. {
  530. $this->exerciseList[]=$exerciseId;
  531. $sql="INSERT INTO $TBL_EXERCICE_QUESTION(question_id,exercice_id) VALUES('$id','$exerciseId')";
  532. api_sql_query($sql,__FILE__,__LINE__);
  533. }
  534. }
  535. /**
  536. * removes an exercise from the exercise list
  537. *
  538. * @author - Olivier Brouckaert
  539. * @param - integer $exerciseId - exercise ID
  540. * @return - boolean - true if removed, otherwise false
  541. */
  542. function removeFromList($exerciseId)
  543. {
  544. global $TBL_EXERCICE_QUESTION;
  545. $id=$this->id;
  546. // searches the position of the exercise ID in the list
  547. $pos=array_search($exerciseId,$this->exerciseList);
  548. // exercise not found
  549. if($pos === false)
  550. {
  551. return false;
  552. }
  553. else
  554. {
  555. // deletes the position in the array containing the wanted exercise ID
  556. unset($this->exerciseList[$pos]);
  557. $sql="DELETE FROM $TBL_EXERCICE_QUESTION WHERE question_id='$id' AND exercice_id='$exerciseId'";
  558. api_sql_query($sql,__FILE__,__LINE__);
  559. return true;
  560. }
  561. }
  562. /**
  563. * deletes a question from the database
  564. * the parameter tells if the question is removed from all exercises (value = 0),
  565. * or just from one exercise (value = exercise ID)
  566. *
  567. * @author - Olivier Brouckaert
  568. * @param - integer $deleteFromEx - exercise ID if the question is only removed from one exercise
  569. */
  570. function delete($deleteFromEx=0)
  571. {
  572. global $TBL_EXERCICE_QUESTION, $TBL_QUESTIONS, $TBL_REPONSES;
  573. $id=$this->id;
  574. // if the question must be removed from all exercises
  575. if(!$deleteFromEx)
  576. {
  577. $sql="DELETE FROM $TBL_EXERCICE_QUESTION WHERE question_id='$id'";
  578. api_sql_query($sql,__FILE__,__LINE__);
  579. $sql="DELETE FROM $TBL_QUESTIONS WHERE id='$id'";
  580. api_sql_query($sql,__FILE__,__LINE__);
  581. $sql="DELETE FROM $TBL_REPONSES WHERE question_id='$id'";
  582. api_sql_query($sql,__FILE__,__LINE__);
  583. $this->removePicture();
  584. // resets the object
  585. $this->Question();
  586. }
  587. // just removes the exercise from the list
  588. else
  589. {
  590. $this->removeFromList($deleteFromEx);
  591. }
  592. }
  593. /**
  594. * duplicates the question
  595. *
  596. * @author - Olivier Brouckaert
  597. * @return - integer - ID of the new question
  598. */
  599. function duplicate()
  600. {
  601. global $TBL_QUESTIONS, $picturePath;
  602. $question=addslashes($this->question);
  603. $description=addslashes($this->description);
  604. $weighting=$this->weighting;
  605. $position=$this->position;
  606. $type=$this->type;
  607. $sql="INSERT INTO $TBL_QUESTIONS(question,description,ponderation,position,type) VALUES('$question','$description','$weighting','$position','$type')";
  608. api_sql_query($sql,__FILE__,__LINE__);
  609. $id=mysql_insert_id();
  610. // duplicates the picture
  611. $this->exportPicture($id);
  612. return $id;
  613. }
  614. /**
  615. * Returns an instance of the class corresponding to the type
  616. * @param integer $type the type of the question
  617. * @return an instance of a Question subclass (or of Questionc class by default)
  618. */
  619. static function getInstance ($type) {
  620. list($file_name,$class_name) = self::$questionTypes[$type];
  621. include_once ($file_name);
  622. if(class_exists($class_name))
  623. {
  624. return new $class_name();
  625. }
  626. else
  627. {
  628. echo 'Can\'t instanciate class '.$class_name.' of type '.$type;
  629. return null;
  630. }
  631. }
  632. /**
  633. * Creates the form to create / edit a question
  634. * A subclass can redifine this function to add fields...
  635. * @param FormValidator $form the formvalidator instance (by reference)
  636. */
  637. function createForm (&$form) {
  638. echo ' <style>
  639. div.row div.label{ width: 10%; }
  640. div.row div.formw{ width: 89%; }
  641. </style>';
  642. // question name
  643. $test=$form->addElement('text','questionName',get_lang('Question'),'size="60"');
  644. $renderer = $form->defaultRenderer();
  645. $renderer->setElementTemplate('<div class="row"><div class="label">{label}</div><div class="formw">{element}</div></div>','questionName');
  646. $form->addRule('questionName', get_lang('GiveQuestion'), 'required');
  647. // question type
  648. $answerType= intval($_REQUEST['answerType']);
  649. $form->addElement('hidden','answerType',$_REQUEST['answerType']);
  650. // html editor
  651. global $fck_attribute;
  652. $fck_attribute = array();
  653. $fck_attribute['Width'] = '100%';
  654. $fck_attribute['Height'] = '300';
  655. $fck_attribute['ToolbarSet'] = 'TestComment';
  656. $fck_attribute['Config']['IMUploadPath'] = 'upload/test/';
  657. $fck_attribute['Config']['FlashUploadPath'] = 'upload/test/';
  658. if(!api_is_allowed_to_edit()) $fck_attribute['Config']['UserStatus'] = 'student';
  659. $form->add_html_editor('questionDescription', get_lang('QuestionDescription'), false);
  660. $renderer->setElementTemplate('<div class="row"><div class="label">{label}</div><div class="formw">{element}</div></div>','questionDescription');
  661. // hidden values
  662. $form->addElement('hidden','myid',$_REQUEST['myid']);
  663. // default values
  664. $defaults = array();
  665. $defaults['questionName'] = $this -> question;
  666. $defaults['questionDescription'] = $this -> description;
  667. $form -> setDefaults($defaults);
  668. }
  669. /**
  670. * function which process the creation of questions
  671. * @param FormValidator $form the formvalidator instance
  672. * @param Exercise $objExercise the Exercise instance
  673. */
  674. function processCreation ($form, $objExercise) {
  675. $this -> updateTitle($form->getSubmitValue('questionName'));
  676. $this -> updateDescription($form->getSubmitValue('questionDescription'));
  677. $this -> save($objExercise -> id);
  678. // modify the exercise
  679. $objExercise->addToList($this -> id);
  680. $objExercise->save();
  681. }
  682. /**
  683. * abstract function which creates the form to create / edit the answers of the question
  684. * @param the formvalidator instance
  685. */
  686. abstract function createAnswersForm ($form);
  687. /**
  688. * abstract function which process the creation of answers
  689. * @param the formvalidator instance
  690. */
  691. abstract function processAnswersCreation ($form);
  692. /**
  693. * Displays the menu of question types
  694. */
  695. static function display_type_menu ()
  696. {
  697. global $exerciseId;
  698. echo '<strong>'.get_lang('AddQ').' :</strong> <div>';
  699. foreach(self::$questionTypes as $i=>$a_type)
  700. {
  701. // include the class of the type
  702. include_once($a_type[0]);
  703. // get the picture of the type and the langvar which describes it
  704. eval('$img = '.$a_type[1].'::$typePicture;');
  705. eval('$explanation = get_lang('.$a_type[1].'::$explanationLangVar);');
  706. echo '
  707. <div id="answer_type_'.$i.'" style="float: left; width:120px; text-align:center">
  708. <a href="admin.php?newQuestion=yes&answerType='.$i.'">
  709. <div>
  710. <img src="'.api_get_path(WEB_IMG_PATH).'/'.$img.'" />
  711. </div>
  712. <div>
  713. '.$explanation.'
  714. </div>
  715. </a>
  716. </div>';
  717. }
  718. echo '
  719. <div id="answer_type_'.$i.'" style="float: left; width:120px; text-align:center">
  720. <a href="question_pool.php?fromExercise='.$exerciseId.'">
  721. <div>
  722. <img src="'.api_get_path(WEB_IMG_PATH).'/database.gif" />
  723. </div>
  724. <div>
  725. '.get_lang('GetExistingQuestion').'
  726. </div>
  727. </a>
  728. </div>
  729. </div>
  730. <div style="clear:both"></div>';
  731. }
  732. }
  733. endif;
  734. ?>