<?php
//============================================================+
// File name   : tce_import_xml_questions.php
// Begin       : 2006-03-12
// Last Update : 2006-06-19
// 
// Description : Import questions from an XML file.
//
// Author: Nicola Asuni
//
// (c) Copyright:
//               Tecnick.com S.r.l.
//               Via Ugo Foscolo n.19
//               09045 Quartu Sant'Elena (CA)
//               ITALY
//               www.tecnick.com
//               info@tecnick.com
//
// License: GNU GENERAL PUBLIC LICENSE v.2
//          http://www.gnu.org/copyleft/gpl.html
//============================================================+

/**
 * Import questions from an XML file to a selected subject.
 * @package com.tecnick.tcexam.admin
 * @author Nicola Asuni
 * @copyright Copyright &copy; 2004-2006, Tecnick.com S.r.l. - Via Ugo Foscolo n.19 - 09045 Quartu Sant'Elena (CA) - ITALY - www.tecnick.com - info@tecnick.com
 * @link www.tecnick.com
 * @since 2006-03-12
 */

/**
 */

require_once('../config/tce_config.php');

$pagelevel = K_AUTH_ADMIN_SUBJECTS;
require_once('../../shared/code/tce_authorization.php');

$thispage_title = $l['t_question_importer'];
require_once('../code/tce_page_header.php');
require_once('../../shared/code/tce_functions_form.php');
require_once('../../shared/code/tce_functions_tcecode.php');
require_once('../code/tce_functions_auth_sql.php');

switch($menu_mode) {

	case "upload": {
		if($_FILES['userfile']['name']) {
			require_once('../code/tce_functions_upload.php');
			// upload file
			$uploadedfile = F_upload_file("userfile", K_PATH_CACHE);
			if ($uploadedfile !== false) {
				$xmlimporter = new XMLQuestionImporter(K_PATH_CACHE.$uploadedfile, $_REQUEST['subject_id']);
				F_print_error("MESSAGE", $l['m_importing_complete']);
			}
		}
		break;
	}

	default: { 
		break;
	}

} //end of switch


// --- Initialize variables

if($formstatus) {
	if(!isset($subject_id) OR empty($subject_id)) {
		$sql = F_select_subjects_sql()." LIMIT 1";
	}
	else {
		$sql = "SELECT * 
			FROM ".K_TABLE_SUBJECTS." 
			WHERE subject_id=".$subject_id." 
			LIMIT 1";
	}
	if($r = F_db_query($sql, $db)) {
		if($m = F_db_fetch_array($r)) {
			$subject_id = $m['subject_id'];
		}
		else {
			$subject_id = 0;
		}
	}
	else {
		F_display_db_error();
	}
}
?>

<div class="container">

<div class="tceformbox">
<form action="<?php echo $_SERVER['SCRIPT_NAME']; ?>" method="post" enctype="multipart/form-data" id="form_importquestions">

<div class="row">
<span class="label">
<label for="subject_id"><?php echo $l['w_subject']; ?></label>
</span>
<span class="formw">
<select name="subject_id" id="subject_id" size="0" title="<?php echo $l['h_subject']; ?>">
<?php
$sql = F_select_subjects_sql();
if($r = F_db_query($sql, $db)) {
	$countitem = 1;
	while($m = F_db_fetch_array($r)) {
		echo "<option value=\"".$m['subject_id']."\"";
		if($m['subject_id'] == $subject_id) {
			echo " selected=\"selected\"";
		}
		echo ">".$countitem.")[";
		if (F_getBoolean($m['subject_enabled'])) {
			echo "+";
		} else {
			echo "-";
		}
		echo "] ".htmlspecialchars($m['subject_name'], ENT_NOQUOTES, $l['a_meta_charset'])."</option>\n";
		$countitem++;
	}
}
else {
	echo "</select></span></div>\n";
	F_display_db_error();
}
?>
</select>
</span>
</div>

<div class="row"><hr /></div>

<div class="row">
<span class="label">
<label for="userfile"><?php echo $l['w_upload_file']; ?></label>
</span>
<span class="formw">
<input type="hidden" name="MAX_FILE_SIZE" value="<?php echo K_MAX_UPLOAD_SIZE ?>" />
<input type="file" name="userfile" id="userfile" size="20" title="<?php echo $l['h_upload_file']; ?>" />
</span>
&nbsp;
</div>

<div class="row">
<br />
<?php
// show buttons by case
if (isset($subject_id) AND ($subject_id > 0)) {
	F_submit_button("upload", $l['w_upload'], $l['h_submit_file']);
}
?>


</div>

</form>

</div>
<?php

echo "<div class=\"pagehelp\">".$l['hp_import_xml_questions']."</div>";
echo "</div>";

require_once('../code/tce_page_footer.php');

// ------------------------------------------------------------

/**
 * This PHP Class imports question data directly from a XML file.
 *
 * @package com.tecnick.tcexam.admin
 * @name XMLQuestionImporter
 * @abstract XML question importer
 * @license http://www.gnu.org/copyleft/lesser.html GPL
 * @author Nicola Asuni [www.tecnick.com]
 * @copyright Copyright (c) 2004-2006 - Tecnick.com S.r.l (www.tecnick.com) - Via Ugo Foscolo n.19 - 09045 Quartu Sant'Elena (CA) - ITALY - www.tecnick.com - info@tecnick.com
 * @version 1.0.000
 */
class XMLQuestionImporter {
	
	/**
	 * @var ID of subject where to import questions
	 * @access private
	 */
	private $subject_id = "";
	
	/**
	 * @var Is TRUE when we are inside a answer element
	 * @access private
	 */
	private $inanswer = false;
	
	/**
	 * @var Current data element.
	 * @access private
	 */
	private $current_element = "";
	
	/**
	 * @var Current data value.
	 * @access private
	 */
	private $current_data = "";
	
	/**
	 * @var Array to store question data.
	 * @access private
	 */
	private $question_data = Array();
	
	/**
	 * @var Array to store answer data.
	 * @access private
	 */
	private $answer_data = Array();
	
	/**
	 * @var ID of last inserted question
	 * @access private
	 */
	private $question_id = 0;
	
	/**
	 * @var ID of last inserted answer (counter)
	 * @access private
	 */
	private $answer_id = 0;
	
	/**
	 * @var XML file
	 * @access private
	 */
	private $xmlfile = 0;
	
	/**
	 * Class constructor.
	 * @param string $xmlfile xml (XML) file name
	 * @param string $subject_id subject ID
	 */
	public function __construct($xmlfile, $subject_id) {
		// set xml file
		$this->xmlfile = $xmlfile;
		// set subject id
		$this->subject_id = strtoupper($subject_id);
		// creates a new XML parser to be used by the other XML functions
		$this->parser = xml_parser_create();
		// the following function allows to use parser inside object
		xml_set_object($this->parser, $this);
		// disable case-folding for this XML parser
		xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
		// sets the element handler functions for the XML parser
		xml_set_element_handler($this->parser, "startElementHandler", "endElementHandler");
		// sets the character data handler function for the XML parser
		xml_set_character_data_handler($this->parser, "segContentHandler");
		// start parsing an XML document
		if(!xml_parse($this->parser, file_get_contents($xmlfile))) {
			die(sprintf("ERROR xmlResourceBundle :: XML error: %s at line %d",
			xml_error_string(xml_get_error_code($this->parser)),
			xml_get_current_line_number($this->parser)));
		}
		// free this XML parser
		xml_parser_free($this->parser);
	}
	
	/**
	 * Class destructor;
	 */
	public function __destruct() {
		// delete uploaded file
		@unlink($this->xmlfile);
	}
	
	/**
	 * Sets the start element handler function for the XML parser parser.start_element_handler.
	 * @param resource $parser The first parameter, parser, is a reference to the XML parser calling the handler.
	 * @param string $name 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. 
	 * @param array $attribs 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. 
	 * @access private
	 */
	private function startElementHandler($parser, $name, $attribs) {
		switch(strtolower($name)) {
			case 'enabled': {
				if ($this->inanswer) {
					$this->current_element = "answer_enabled";
				} else {
					$this->current_element = "question_enabled";
				}
				$this->current_data = "";
				break;
			}
			case 'description': {
				if ($this->inanswer) {
					$this->current_element = "answer_description";
				} else {
					$this->current_element = "question_description";
				}
				$this->current_data = "";
				break;
			}
			case 'type': {
				$this->current_element = "question_type";
				$this->current_data = "";
				break;
			}
			case 'difficulty': {
				$this->current_element = "question_difficulty";
				$this->current_data = "";
				break;
			}
			case 'isright': {
				$this->current_element = "answer_isright";
				$this->current_data = "";
				break;
			}
			case 'answer': {
				$this->inanswer = true;
				$this->answer_data[$this->answer_id] = Array();
				$this->current_data = "";
				break;
			}
			default: {
				break;
			}
		}
	}
	
	/**
	 * Sets the end element handler function for the XML parser parser.end_element_handler.
	 * @param resource $parser The first parameter, parser, is a reference to the XML parser calling the handler.
	 * @param string $name 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. 
	 * @access private
	 */
	private function endElementHandler($parser, $name) {
		global $l, $db;
		require_once('../config/tce_config.php');
		
		switch(strtolower($name)) {
			case 'isright':
			case 'difficulty':
			case 'type':
			case 'description':
			case 'enabled': {
				$this->current_data = addslashes(F_xml_to_text($this->current_data));
				if ($this->inanswer) {
					$this->answer_data[$this->answer_id][$this->current_element] = $this->current_data;
				} else {
					$this->question_data[$this->current_element] = $this->current_data;
				}
				$this->current_element = "";
				$this->current_data = "";
				break;
			}
			case 'question': {
				$boolval = Array("false" => "0", "true" => "1");
				$qtype = Array("single" => "1", "multiple" => "2", "text" => "3");
				// add data to database
				// check if alternate key is unique
				$sql = "SELECT question_id 
					FROM ".K_TABLE_QUESTIONS." 
					WHERE question_description='".$this->question_data['question_description']."' 
					AND question_subject_id=".$this->subject_id."
					LIMIT 1";
				if($r = F_db_query($sql, $db)) {
					if($m = F_db_fetch_array($r)) {
						// the question has been already added
						$this->question_id = $m['question_id'];
					} else {
						// add new question
						$sqli = "INSERT INTO ".K_TABLE_QUESTIONS." (
							question_subject_id,
							question_description,
							question_type,
							question_difficulty,
							question_enabled
							) VALUES (
							".$this->subject_id.", 
							'".$this->question_data['question_description']."', 
							'".$qtype[$this->question_data['question_type']]."',
							'".$this->question_data['question_difficulty']."',
							'".$boolval[$this->question_data['question_enabled']]."')";
						if(!$ri = F_db_query($sqli, $db)) {
							F_display_db_error(false);
						}
						else {
							$this->question_id = F_db_insert_id($db, K_TABLE_QUESTIONS, 'question_id');
						}
					}
				}
				else {
					F_display_db_error();
				}
				
				// insert answers
				while (list($akey, $aval) = each($this->answer_data)) {
					// check if alternate key is unique
					if(isset($this->answer_data[$akey]['answer_description'])
						AND (!empty($this->answer_data[$akey]['answer_description']))
						AND F_check_unique(K_TABLE_ANSWERS,
						 "answer_description='".$this->answer_data[$akey]['answer_description']."' 
							AND answer_question_id=".$this->question_id."")
						) {
						$sql = "INSERT INTO ".K_TABLE_ANSWERS." (
							answer_question_id,
							answer_description,
							answer_isright,
							answer_enabled
							) VALUES (
							".$this->question_id.",
							'".$this->answer_data[$akey]['answer_description']."',
							'".$boolval[$this->answer_data[$akey]['answer_isright']]."',
							'".$boolval[$this->answer_data[$akey]['answer_enabled']]."')";
						if(!$r = F_db_query($sql, $db)) {
							F_display_db_error(false);
						}
					}
				}
				
				$this->answer_data = Array();
				$this->question_data = Array();
				$this->answer_id = 0;
				break;
			}
			case 'answer': {
				$this->answer_id++;
				$this->answer_data[$this->answer_id] = Array();
				$this->inanswer = false;
				break;
			}
			default: {
				break;
			}
		}
	}
	
	/**
	 * Sets the character data handler function for the XML parser parser.handler.
	 * @param resource $parser The first parameter, parser, is a reference to the XML parser calling the handler.
	 * @param string $data The second parameter, data, contains the character data as a string. 
	 * @access private
	 */
	private function segContentHandler($parser, $data) {
		if (strlen($this->current_element) > 0) {
			// we are inside an element
			$this->current_data .= $data;
		}
	}
	
} // END OF CLASS

//============================================================+
// END OF FILE                                                 
//============================================================+
?>
