source-class-XMLQuestionImporter

It appears that you are using AdBlocking software. The cost of running this website is covered by advertisements. If you like it please feel free to a small amount of money to secure the future of this website.
Overview

Classes

Interfaces

Exceptions

Functions

  1: <?php
  2: //============================================================+
  3: // File name   : tce_class_import_xml.php
  4: // Begin       : 2006-03-12
  5: // Last Update : 2012-11-14
  6: //
  7: // Description : Class to import questions from an XML file.
  8: //
  9: // Author: Nicola Asuni
 10: //
 11: // (c) Copyright:
 12: //               Nicola Asuni
 13: //               Tecnick.com LTD
 14: //               www.tecnick.com
 15: //               info@tecnick.com
 16: //
 17: // License:
 18: //    Copyright (C) 2004-2012  Nicola Asuni - Tecnick.com LTD
 19: //    See LICENSE.TXT file for more information.
 20: //============================================================+
 21: 
 22: /**
 23:  * @file
 24:  * Class to import questions from an XML file.
 25:  * @package com.tecnick.tcexam.admin
 26:  * @author Nicola Asuni
 27:  * @since 2006-03-12
 28:  */
 29: 
 30: /**
 31:  * @class XMLQuestionImporter
 32:  * This PHP Class imports question data directly from an XML file.
 33:  * @package com.tecnick.tcexam.admin
 34:  * @version 1.1.000
 35:  */
 36: class XMLQuestionImporter
 37: {
 38: 
 39:     /**
 40:      * XML file.
 41:      * @private
 42:      */
 43:     private $xmlfile = 0;
 44: 
 45:     /**
 46:      * Current level: 'module', 'subject', 'question', 'answer'.
 47:      * @private
 48:      */
 49:     private $level = '';
 50: 
 51:     /**
 52:      * Array to store current level data.
 53:      * @private
 54:      */
 55:     private $level_data = array();
 56: 
 57:     /**
 58:      * Current data element.
 59:      * @private
 60:      */
 61:     private $current_element = '';
 62: 
 63:     /**
 64:      * Current data value.
 65:      * @private
 66:      */
 67:     private $current_data = '';
 68: 
 69:     /**
 70:      * Boolean values.
 71:      * @private
 72:      */
 73:     private $boolval = array('false' => '0', 'true' => '1');
 74: 
 75:     /**
 76:      * Type of questions.
 77:      * @private
 78:      */
 79:     private $qtype = array('single' => '1', 'multiple' => '2', 'text' => '3', 'ordering' => '4');
 80: 
 81:     /**
 82:      * Store hash values of question descriptions.
 83:      * This is used to avoid the 255 chars limitation for string indexes on MySQL
 84:      * @private
 85:      */
 86:     private $questionhash = array();
 87: 
 88:     /**
 89:      * Class constructor.
 90:      * @param $xmlfile (string) xml (XML) file name
 91:      * @return true or die for parsing error
 92:      */
 93:     public function __construct($xmlfile)
 94:     {
 95:         // set xml file
 96:         $this->xmlfile = $xmlfile;
 97:         // creates a new XML parser to be used by the other XML functions
 98:         $this->parser = xml_parser_create();
 99:         // the following function allows to use parser inside object
100:         xml_set_object($this->parser, $this);
101:         // disable case-folding for this XML parser
102:         xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
103:         // sets the element handler functions for the XML parser
104:         xml_set_element_handler($this->parser, 'startElementHandler', 'endElementHandler');
105:         // sets the character data handler function for the XML parser
106:         xml_set_character_data_handler($this->parser, 'segContentHandler');
107:         // start parsing an XML document
108:         if (!xml_parse($this->parser, file_get_contents($xmlfile))) {
109:             die(sprintf(
110:                 'ERROR xmlResourceBundle :: XML error: %s at line %d',
111:                 xml_error_string(xml_get_error_code($this->parser)),
112:                 xml_get_current_line_number($this->parser)
113:             ));
114:         }
115:         // free this XML parser
116:         xml_parser_free($this->parser);
117:         return true;
118:     }
119: 
120:     /**
121:      * Class destructor;
122:      */
123:     public function __destruct()
124:     {
125:         // delete uploaded file
126:         @unlink($this->xmlfile);
127:     }
128: 
129:     /**
130:      * Sets the start element handler function for the XML parser parser.start_element_handler.
131:      * @param $parser (resource) The first parameter, parser, is a reference to the XML parser calling the handler.
132:      * @param $name (string) The second parameter, name, contains the name of the element for which this handler is called. If case-folding is in effect for this parser, the element name will be in uppercase letters.
133:      * @param $attribs (array) The third parameter, attribs, contains an associative array with the element's attributes (if any). The keys of this array are the attribute names, the values are the attribute values. Attribute names are case-folded on the same criteria as element names. Attribute values are not case-folded. The original order of the attributes can be retrieved by walking through attribs the normal way, using each(). The first key in the array was the first attribute, and so on.
134:      * @private
135:      */
136:     private function startElementHandler($parser, $name, $attribs)
137:     {
138:         $name = strtolower($name);
139:         switch ($name) {
140:             case 'module':
141:             case 'subject':
142:             case 'question':
143:             case 'answer': {
144:                 $this->level = $name;
145:                 $this->level_data[$name] = array();
146:                 $this->current_data = '';
147:                 switch ($name) {
148:                     case 'module': {
149:                         $this->level_data['module']['module_name'] = 'default';
150:                         $this->level_data['module']['module_enabled'] = 'false';
151:                         $this->level_data['module']['module_user_id'] = '1';
152:                         break;
153:                     }
154:                     case 'subject': {
155:                         $this->addModule();
156:                         $this->level_data['subject']['subject_name'] = 'default';
157:                         $this->level_data['subject']['subject_description'] = 'default';
158:                         $this->level_data['subject']['subject_enabled'] = 'false';
159:                         $this->level_data['subject']['subject_user_id'] = '1';
160:                         $this->level_data['subject']['subject_module_id'] = '1';
161:                         break;
162:                     }
163:                     case 'question': {
164:                         $this->addSubject();
165:                         $this->level_data['question']['question_subject_id'] = '1';
166:                         $this->level_data['question']['question_description'] = 'default';
167:                         $this->level_data['question']['question_explanation'] = '';
168:                         $this->level_data['question']['question_type'] = 'single';
169:                         $this->level_data['question']['question_difficulty'] = '0';
170:                         $this->level_data['question']['question_enabled'] = 'false';
171:                         $this->level_data['question']['question_position'] = 0;
172:                         $this->level_data['question']['question_timer'] = 0;
173:                         $this->level_data['question']['question_fullscreen'] = 'false';
174:                         $this->level_data['question']['question_inline_answers'] = 'false';
175:                         $this->level_data['question']['question_auto_next'] = 'false';
176:                         break;
177:                     }
178:                     case 'answer': {
179:                         $this->addQuestion();
180:                         $this->level_data['answer']['answer_question_id'] = '1';
181:                         $this->level_data['answer']['answer_description'] = 'default';
182:                         $this->level_data['answer']['answer_explanation'] = '';
183:                         $this->level_data['answer']['answer_isright'] = 'false';
184:                         $this->level_data['answer']['answer_enabled'] = 'false';
185:                         $this->level_data['answer']['answer_position'] = '0';
186:                         $this->level_data['answer']['answer_keyboard_key'] = '';
187:                         break;
188:                     }
189:                 }
190:                 break;
191:             }
192:             default: {
193:                 $this->current_element = $this->level.'_'.$name;
194:                 $this->current_data = '';
195:                 break;
196:             }
197:         }
198:     }
199: 
200:     /**
201:      * Sets the end element handler function for the XML parser parser.end_element_handler.
202:      * @param $parser (resource) The first parameter, parser, is a reference to the XML parser calling the handler.
203:      * @param $name (string) The second parameter, name, contains the name of the element for which this handler is called. If case-folding is in effect for this parser, the element name will be in uppercase letters.
204:      * @private
205:      */
206:     private function endElementHandler($parser, $name)
207:     {
208:         global $l, $db;
209:         require_once('../config/tce_config.php');
210:         $name = strtolower($name);
211:         switch ($name) {
212:             case 'module': {
213:                 $this->addModule();
214:                 $this->level = '';
215:                 break;
216:             }
217:             case 'subject': {
218:                 $this->addSubject();
219:                 $this->level = 'module';
220:                 break;
221:             }
222:             case 'question': {
223:                 $this->addQuestion();
224:                 $this->level = 'subject';
225:                 break;
226:             }
227:             case 'answer': {
228:                 $this->addAnswer();
229:                 $this->level = 'question';
230:                 break;
231:             }
232:             default: {
233:                 $elname = $this->level.'_'.$name;
234:                 if ($this->current_element == $elname) {
235:                     // convert XML special chars
236:                     $this->level_data[$this->level][$this->current_element] = F_xml_to_text(utrim($this->current_data));
237:                     if (($this->current_element == 'question_description') or ($this->current_element == 'answer_description')) {
238:                         // normalize UTF-8 string based on settings
239:                         $this->level_data[$this->level][$this->current_element] = F_utf8_normalizer($this->level_data[$this->level][$this->current_element], K_UTF8_NORMALIZATION_MODE);
240:                     }
241:                     // escape for SQL
242:                     $this->level_data[$this->level][$this->current_element] = F_escape_sql($db, $this->level_data[$this->level][$this->current_element], false);
243:                 }
244:                 break;
245:             }
246:         }
247:     }
248: 
249:     /**
250:      * Sets the character data handler function for the XML parser parser.handler.
251:      * @param $parser (resource) The first parameter, parser, is a reference to the XML parser calling the handler.
252:      * @param $data (string) The second parameter, data, contains the character data as a string.
253:      * @private
254:      */
255:     private function segContentHandler($parser, $data)
256:     {
257:         if (strlen($this->current_element) > 0) {
258:             // we are inside an element
259:             $this->current_data .= $data;
260:         }
261:     }
262: 
263:     /**
264:      * Add a new module if not exist.
265:      * @private
266:      */
267:     private function addModule()
268:     {
269:         global $l, $db;
270:         require_once('../config/tce_config.php');
271:         require_once('../../shared/code/tce_functions_auth_sql.php');
272:         if (isset($this->level_data['module']['module_id']) and ($this->level_data['module']['module_id'] > 0)) {
273:             return;
274:         }
275:         // check if this module already exist
276:         $sql = 'SELECT module_id
277:             FROM '.K_TABLE_MODULES.'
278:             WHERE module_name=\''.$this->level_data['module']['module_name'].'\'
279:             LIMIT 1';
280:         if ($r = F_db_query($sql, $db)) {
281:             if ($m = F_db_fetch_array($r)) {
282:                 // get existing module ID
283:                 if (!F_isAuthorizedUser(K_TABLE_MODULES, 'module_id', $m['module_id'], 'module_user_id')) {
284:                     // unauthorized user
285:                     $this->level_data['module']['module_id'] = false;
286:                 } else {
287:                     $this->level_data['module']['module_id'] = $m['module_id'];
288:                 }
289:             } else {
290:                 // insert new module
291:                 $sql = 'INSERT INTO '.K_TABLE_MODULES.' (
292:                     module_name,
293:                     module_enabled,
294:                     module_user_id
295:                     ) VALUES (
296:                     \''.$this->level_data['module']['module_name'].'\',
297:                     \''.$this->boolval[$this->level_data['module']['module_enabled']].'\',
298:                     \''.$_SESSION['session_user_id'].'\'
299:                     )';
300:                 if (!$r = F_db_query($sql, $db)) {
301:                     F_display_db_error();
302:                 } else {
303:                     // get new module ID
304:                     $this->level_data['module']['module_id'] = F_db_insert_id($db, K_TABLE_MODULES, 'module_id');
305:                 }
306:             }
307:         } else {
308:             F_display_db_error();
309:         }
310:     }
311: 
312:     /**
313:      * Add a new subject if not exist.
314:      * @private
315:      */
316:     private function addSubject()
317:     {
318:         global $l, $db;
319:         require_once('../config/tce_config.php');
320:         if ($this->level_data['module']['module_id'] === false) {
321:             return;
322:         }
323:         if (isset($this->level_data['subject']['subject_id']) and ($this->level_data['subject']['subject_id'] > 0)) {
324:             return;
325:         }
326:         // check if this subject already exist
327:         $sql = 'SELECT subject_id
328:             FROM '.K_TABLE_SUBJECTS.'
329:             WHERE subject_name=\''.$this->level_data['subject']['subject_name'].'\'
330:                 AND subject_module_id='.$this->level_data['module']['module_id'].'
331:             LIMIT 1';
332:         if ($r = F_db_query($sql, $db)) {
333:             if ($m = F_db_fetch_array($r)) {
334:                 // get existing subject ID
335:                 $this->level_data['subject']['subject_id'] = $m['subject_id'];
336:             } elseif ($this->level_data['module']['module_id'] !== false) {
337:                 // insert new subject
338:                 $sql = 'INSERT INTO '.K_TABLE_SUBJECTS.' (
339:                     subject_name,
340:                     subject_description,
341:                     subject_enabled,
342:                     subject_user_id,
343:                     subject_module_id
344:                     ) VALUES (
345:                     \''.$this->level_data['subject']['subject_name'].'\',
346:                     '.F_empty_to_null($this->level_data['subject']['subject_description']).',
347:                     \''.$this->boolval[$this->level_data['subject']['subject_enabled']].'\',
348:                     \''.$_SESSION['session_user_id'].'\',
349:                     '.$this->level_data['module']['module_id'].'
350:                     )';
351:                 if (!$r = F_db_query($sql, $db)) {
352:                     F_display_db_error();
353:                 } else {
354:                     // get new subject ID
355:                     $this->level_data['subject']['subject_id'] = F_db_insert_id($db, K_TABLE_SUBJECTS, 'subject_id');
356:                 }
357:             } else {
358:                 $this->level_data['subject']['subject_id'] = false;
359:             }
360:         } else {
361:             F_display_db_error();
362:         }
363:     }
364: 
365:     /**
366:      * Add a new question if not exist.
367:      * @private
368:      */
369:     private function addQuestion()
370:     {
371:         global $l, $db;
372:         require_once('../config/tce_config.php');
373:         if ($this->level_data['module']['module_id'] === false) {
374:             return;
375:         }
376:         if ($this->level_data['subject']['subject_id'] === false) {
377:             return;
378:         }
379:         if (isset($this->level_data['question']['question_id']) and ($this->level_data['question']['question_id'] > 0)) {
380:             return;
381:         }
382:         // check if this question already exist
383:         $sql = 'SELECT question_id
384:             FROM '.K_TABLE_QUESTIONS.'
385:             WHERE ';
386:         if (K_DATABASE_TYPE == 'ORACLE') {
387:             $sql .= 'dbms_lob.instr(question_description,\''.$this->level_data['question']['question_description'].'\',1,1)>0';
388:         } elseif ((K_DATABASE_TYPE == 'MYSQL') and K_MYSQL_QA_BIN_UNIQUITY) {
389:             $sql .= 'question_description=\''.$this->level_data['question']['question_description'].'\' COLLATE utf8_bin';
390:         } else {
391:             $sql .= 'question_description=\''.$this->level_data['question']['question_description'].'\'';
392:         }
393:         $sql .= ' AND question_subject_id='.$this->level_data['subject']['subject_id'].' LIMIT 1';
394:         if ($r = F_db_query($sql, $db)) {
395:             if ($m = F_db_fetch_array($r)) {
396:                 // get existing question ID
397:                 $this->level_data['question']['question_id'] = $m['question_id'];
398:                 return;
399:             }
400:         } else {
401:             F_display_db_error();
402:         }
403:         if (K_DATABASE_TYPE == 'MYSQL') {
404:             // this section is to avoid the problems on MySQL string comparison
405:             $maxkey = 240;
406:             $strkeylimit = min($maxkey, strlen($this->level_data['question']['question_description']));
407:             $stop = $maxkey / 3;
408:             while (in_array(md5(strtolower(substr($this->level_data['subject']['subject_id'].$this->level_data['question']['question_description'], 0, $strkeylimit))), $this->questionhash) and ($stop > 0)) {
409:                 // a similar question was already imported from this XML, so we change it a little bit to avoid duplicate keys
410:                 $this->level_data['question']['question_description'] = '_'.$this->level_data['question']['question_description'];
411:                 $strkeylimit = min($maxkey, ($strkeylimit + 1));
412:                 $stop--; // variable used to avoid infinite loop
413:             }
414:             if ($stop == 0) {
415:                 F_print_error('ERROR', 'Unable to get unique question ID');
416:                 return;
417:             }
418:         }
419:         $sql = 'START TRANSACTION';
420:         if (!$r = F_db_query($sql, $db)) {
421:             F_display_db_error();
422:         }
423:         // insert question
424:         $sql = 'INSERT INTO '.K_TABLE_QUESTIONS.' (
425:             question_subject_id,
426:             question_description,
427:             question_explanation,
428:             question_type,
429:             question_difficulty,
430:             question_enabled,
431:             question_position,
432:             question_timer,
433:             question_fullscreen,
434:             question_inline_answers,
435:             question_auto_next
436:             ) VALUES (
437:             '.$this->level_data['subject']['subject_id'].',
438:             \''.$this->level_data['question']['question_description'].'\',
439:             '.F_empty_to_null($this->level_data['question']['question_explanation']).',
440:             \''.$this->qtype[$this->level_data['question']['question_type']].'\',
441:             \''.$this->level_data['question']['question_difficulty'].'\',
442:             \''.$this->boolval[$this->level_data['question']['question_enabled']].'\',
443:             '.F_zero_to_null($this->level_data['question']['question_position']).',
444:             \''.$this->level_data['question']['question_timer'].'\',
445:             \''.$this->boolval[$this->level_data['question']['question_fullscreen']].'\',
446:             \''.$this->boolval[$this->level_data['question']['question_inline_answers']].'\',
447:             \''.$this->boolval[$this->level_data['question']['question_auto_next']].'\'
448:             )';
449:         if (!$r = F_db_query($sql, $db)) {
450:             F_display_db_error(false);
451:         } else {
452:             // get new question ID
453:             $this->level_data['question']['question_id'] = F_db_insert_id($db, K_TABLE_QUESTIONS, 'question_id');
454:             if (K_DATABASE_TYPE == 'MYSQL') {
455:                 $this->questionhash[] = md5(strtolower(substr($this->level_data['subject']['subject_id'].$this->level_data['question']['question_description'], 0, $strkeylimit)));
456:             }
457:         }
458:         $sql = 'COMMIT';
459:         if (!$r = F_db_query($sql, $db)) {
460:             F_display_db_error();
461:         }
462:     }
463: 
464:     /**
465:      * Add a new answer if not exist.
466:      * @private
467:      */
468:     private function addAnswer()
469:     {
470:         global $l, $db;
471:         require_once('../config/tce_config.php');
472:         if ($this->level_data['module']['module_id'] === false) {
473:             return;
474:         }
475:         if ($this->level_data['subject']['subject_id'] === false) {
476:             return;
477:         }
478:         if (isset($this->level_data['answer']['answer_id']) and ($this->level_data['answer']['answer_id'] > 0)) {
479:             return;
480:         }
481:         // check if this answer already exist
482:         $sql = 'SELECT answer_id
483:             FROM '.K_TABLE_ANSWERS.'
484:             WHERE ';
485:         if (K_DATABASE_TYPE == 'ORACLE') {
486:             $sql .= 'dbms_lob.instr(answer_description, \''.$this->level_data['answer']['answer_description'].'\',1,1)>0';
487:         } elseif ((K_DATABASE_TYPE == 'MYSQL') and K_MYSQL_QA_BIN_UNIQUITY) {
488:             $sql .= 'answer_description=\''.$this->level_data['answer']['answer_description'].'\' COLLATE utf8_bin';
489:         } else {
490:             $sql .= 'answer_description=\''.$this->level_data['answer']['answer_description'].'\'';
491:         }
492:         $sql .= ' AND answer_question_id='.$this->level_data['question']['question_id'].' LIMIT 1';
493:         if ($r = F_db_query($sql, $db)) {
494:             if ($m = F_db_fetch_array($r)) {
495:                 // get existing subject ID
496:                 $this->level_data['answer']['answer_id'] = $m['answer_id'];
497:             } else {
498:                 $sql = 'START TRANSACTION';
499:                 if (!$r = F_db_query($sql, $db)) {
500:                     F_display_db_error();
501:                 }
502:                 $sql = 'INSERT INTO '.K_TABLE_ANSWERS.' (
503:                     answer_question_id,
504:                     answer_description,
505:                     answer_explanation,
506:                     answer_isright,
507:                     answer_enabled,
508:                     answer_position,
509:                     answer_keyboard_key
510:                     ) VALUES (
511:                     '.$this->level_data['question']['question_id'].',
512:                     \''.$this->level_data['answer']['answer_description'].'\',
513:                     '.F_empty_to_null($this->level_data['answer']['answer_explanation']).',
514:                     \''.$this->boolval[$this->level_data['answer']['answer_isright']].'\',
515:                     \''.$this->boolval[$this->level_data['answer']['answer_enabled']].'\',
516:                     '.F_zero_to_null($this->level_data['answer']['answer_position']).',
517:                     '.F_empty_to_null($this->level_data['answer']['answer_keyboard_key']).'
518:                     )';
519:                 if (!$r = F_db_query($sql, $db)) {
520:                     F_display_db_error(false);
521:                     F_db_query('ROLLBACK', $db);
522:                 } else {
523:                     // get new answer ID
524:                     $this->level_data['answer']['answer_id'] = F_db_insert_id($db, K_TABLE_ANSWERS, 'answer_id');
525:                 }
526:                 $sql = 'COMMIT';
527:                 if (!$r = F_db_query($sql, $db)) {
528:                     F_display_db_error();
529:                 }
530:             }
531:         } else {
532:             F_display_db_error();
533:         }
534:     }
535: } // END OF CLASS
536: 
537: //============================================================+
538: // END OF FILE
539: //============================================================+
540: 
 

© 2004-2018 – Nicola Asuni - Tecnick.com - All rights reserved.
about - disclaimer - privacy