Index: branches/5.2.x/core/units/helpers/xml_helper.php
===================================================================
--- branches/5.2.x/core/units/helpers/xml_helper.php	(revision 14606)
+++ branches/5.2.x/core/units/helpers/xml_helper.php	(revision 14607)
@@ -1,572 +1,606 @@
 <?php
 /**
 * @version	$Id$
 * @package	In-Portal
 * @copyright	Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
 * @license      GNU/GPL
 * In-Portal is Open Source software.
 * This means that this software may have been modified pursuant
 * the GNU General Public License, and as distributed it includes
 * or is derivative of works licensed under the GNU General Public License
 * or other free or open source software licenses.
 * See http://www.in-portal.org/license for copyright notices and details.
 */
 
 	defined('FULL_PATH') or die('restricted access!');
 
 	class kXMLHelper extends kHelper {
 
 		/**
 		 * Normal mode for XMLHelper
 		 *
 		 */
 		const XML_NO_TEXT_NODES = 1;
 
 		/**
 		 * Will create text nodes for every char-data (used in kPDFHelper)
 		 *
 		 */
 		const XML_WITH_TEXT_NODES =  2;
 
-		var $RootElement = null;
+		/**
+		 * Root node after parsing xml document
+		 *
+		 * @var kXMLNode
+		 * @access protected
+		 */
+		protected $RootElement = null;
 
 		/**
-		 * Enter description here...
+		 * Xml node, that is currently being processed
 		 *
 		 * @var kXMLNode
+		 * @access protected
 		 */
-		var $CurrentElement = null;
+		protected $CurrentElement = null;
 
 		var $Mode;
 
 		var $XMLNodeClassName = 'kXMLNode';
 
 		public function __construct()
 		{
 			parent::__construct();
 
 			if ( version_compare(PHP_VERSION, '5.0.0') === 1 ) {
 				$this->XMLNodeClassName = 'kXMLNode5';
 				kUtil::includeOnce( dirname(__FILE__) . DIRECTORY_SEPARATOR . 'xml_helper5.php' );
 			}
 		}
 
 		/**
 		 * Parses XML data specified and returns root node
 		 *
 		 * @param string $xml
 		 * @param int $mode
 		 * @param bool $no_case_folding
 		 * @return kXMLNode
+		 * @access public
 		 */
-		function &Parse($xml = null, $mode = self::XML_NO_TEXT_NODES, $no_case_folding = false)
+		public function &Parse($xml = null, $mode = self::XML_NO_TEXT_NODES, $no_case_folding = false)
 		{
 			$xml = trim($xml);
 			$this->Mode = !isset($mode) ? self::XML_NO_TEXT_NODES : $mode;
 			$this->Clear(); // in case if Parse method is called more then one time
 			$xml_parser = xml_parser_create();
 
-			if ($no_case_folding) {
+			if ( $no_case_folding ) {
 				xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, 0);
 			}
 
-			xml_set_element_handler( $xml_parser, Array(&$this, 'startElement'), Array(&$this, 'endElement') );
-			xml_set_character_data_handler( $xml_parser, Array(&$this, 'characterData') );
+			xml_set_element_handler($xml_parser, Array (&$this, 'startElement'), Array (&$this, 'endElement'));
+			xml_set_character_data_handler($xml_parser, Array (&$this, 'characterData'));
 
 			if ( !xml_parse($xml_parser, $xml, 1) ) {
 				$class_name = $this->XMLNodeClassName;
 				$byte = xml_get_current_byte_index($xml_parser);
-				$extract = '...' . mb_substr($xml, $byte-50, 50) . ' !!![' . mb_substr($xml, $byte, 1) . ']!!! '.mb_substr($xml, $byte+1, 50) . '...';
+				$extract = '...' . mb_substr($xml, $byte - 50, 50) . ' !!![' . mb_substr($xml, $byte, 1) . ']!!! ' . mb_substr($xml, $byte + 1, 50) . '...';
 
 				$message = sprintf(
 					'XML error number %s: %s at line %d col %d, byte %d, extract: %s',
 					xml_get_error_code($xml_parser),
-					xml_error_string(xml_get_error_code($xml_parser)),
+					xml_error_string( xml_get_error_code($xml_parser) ),
 					xml_get_current_line_number($xml_parser),
 					xml_get_current_column_number($xml_parser),
 					xml_get_current_byte_index($xml_parser),
 					$extract
 				);
 
-				$this->RootElement =& new $class_name(
-					'ERROR',
-					array(
-						'code' => xml_get_error_code($xml_parser),
-						'message' => $message
-					)
-				);
+				$this->RootElement =& new $class_name('ERROR', array ('code' => xml_get_error_code($xml_parser), 'message' => $message));
 
-			    trigger_error($message, E_USER_WARNING);
+				trigger_error($message, E_USER_WARNING);
 			}
 
 			xml_parser_free($xml_parser);
 
 			$root_copy = $this->RootElement;
+			/* @var $root_copy kXMLNode */
+
 			unset($this->RootElement);
 			unset($this->CurrentElement);
 
 			return $root_copy;
 		}
 
 		function ConvertHTMLEntities($s)
 		{
 			//build first an assoc. array with the entities we want to match
 			$table1 = get_html_translation_table(HTML_ENTITIES, ENT_QUOTES);
 
 			$patterns = array();
 			$replacements = array();
 			//now build another assoc. array with the entities we want to replace (numeric entities)
 			foreach ($table1 as $k=>$v){
 			  $patterns[] = "/$v/";
 		//	  $c = htmlentities($k,ENT_QUOTES,"UTF-8");
 			  $replacements[] = "&#".ord($k).";";
 			}
 
 			//now perform a replacement using preg_replace
 			//each matched value in array 1 will be replaced with the corresponding value in array 2
 			$s = preg_replace($patterns,$replacements,$s);
 
 			return $s;
 		}
 
 		function startElement(&$Parser, &$Elem, $Attrs)
 		{
 			$parent =& $this->CurrentElement; // 1. $parent is now reference to $this->CurrentElement
 			$class_name = $this->XMLNodeClassName;
 			$this->CurrentElement =& new $class_name($Elem, $Attrs); // 2. =& ensures, that new object won't be assigned to $parent as well (don't remove)
 
 			if (!isset($this->RootElement) || is_null($this->RootElement)) {
 				$this->RootElement =& $this->CurrentElement;
 			}
 
 			if (!is_null($parent)) {
 				$parent->AddChild($this->CurrentElement);
 			}
 		}
 
 		function characterData($Parser, $Line)
 		{
 			if ($this->Mode == self::XML_WITH_TEXT_NODES) {
 				$class_name = $this->XMLNodeClassName;
 				$text_node = new $class_name('_TEXT_');
+				/* @var $text_node kXMLNode */
+
 				$text_node->AppendData($Line);
 				$this->CurrentElement->AddChild( $text_node );
 			}
 			else {
 				$this->CurrentElement->AppendData($Line);
 			}
 		}
 
 		function endElement($Parser, $Elem)
 		{
 			if ($this->Mode == self::XML_WITH_TEXT_NODES) {
 				/*if (count($this->CurrentElement->Children) == 1 && $this->CurrentElement->firstChild->Name == '_TEXT_') {
 					$this->CurrentElement->Children = array();
 				}*/
 			}
 
 			if ($this->CurrentElement->Parent != null) {
 				$this->CurrentElement =& $this->CurrentElement->Parent;
 			}
 		}
 
 		function Clear()
 		{
 			unset($this->RootElement);
 			unset($this->CurrentElement);
 		}
 
 		function &CreateNode($name, $value=null, $attributes=array())
 		{
 			$class_name = $this->XMLNodeClassName;
 			$node = new $class_name($name, $attributes);
 			/* @var $node kXMLNode */
 
 			if ($value) {
 				$node->SetData($value);
 			}
 			return $node;
 		}
+
+		/**
+		 * Checks, that there is no error during XML document parsing
+		 *
+		 * @param kXMLNode $root_node
+		 * @param string $root_node_name
+		 * @return bool
+		 * @access public
+		 */
+		public function isError(&$root_node, $root_node_name)
+		{
+			if ( !is_object($root_node) || !preg_match('/^kxmlnode/i', get_class($root_node)) || ($root_node->Name == 'ERROR') || ($root_node->Name != $root_node_name) ) {
+				return true;
+			}
+
+			return false;
+		}
 	}
 
 	class kXMLNode {
 
 		/**
 		 * Casefolded name of this node
 		 *
 		 * @var string
 		 */
 		var $Name = null;
 
 		/**
 		 * Original name of this node
 		 *
 		 * @var string
 		 */
 		var $OriginalName = null;
 
 		/**
 		 * Casefolded attributes of this node
 		 *
 		 * @var Array
 		 */
 		var $Attributes = array();
 
 		/**
 		 * Original attributes of this node
 		 *
 		 * @var Array
 		 */
 		var $OriginalAttributes = array();
 
 		/**
-		 * List of node childnodes
+		 * List of node child nodes
 		 *
 		 * @var Array
+		 * @access public
 		 */
-		var $Children = array();
+		public $Children = Array ();
 
 		/**
 		 * Node content (usually text)
 		 *
 		 * @var string
 		 */
 		var $Data = null;
 
 		/**
 		 * Reference to first child
 		 *
 		 * @var kXMLNode
 		 */
 		var $firstChild = null;
 
 		/**
 		 * Last child of this node
 		 *
 		 * @var kXMLNode
 		 */
 		var $lastChild = null;
 
 		/**
 		 * Parent node
 		 *
 		 * @var kXMLNode
 		 */
 		var $Parent = null;
 
 		/**
 		 * Node position relative to other nodes of it's parent
 		 *
 		 * @var int
 		 */
 		var $Position = 0;
 
 		/**
 		 * Node identifier
 		 *
 		 * @var int
 		 */
 		var $CRC = null;
 
-		function kXMLNode($name, $attrs = array())
+		function __construct($name, $attributes = Array())
 		{
 			$this->Name = strtoupper($name);
 			$this->OriginalName = $name;
-			$this->OriginalAttributes = $attrs;
+			$this->OriginalAttributes = $attributes;
 
-			foreach ($attrs as $attr => $value) {
-				$this->Attributes[ strtoupper($attr) ] = $value;
+			foreach ($attributes as $attr => $value) {
+				$this->Attributes[strtoupper($attr)] = $value;
 			}
 
-			$this->CRC = crc32($this->Name.join(array_keys($this->Attributes)).join(array_values($this->Attributes)));
+			$this->CRC = crc32($this->Name . implode('', array_keys($this->Attributes)) . implode('', array_values($this->Attributes)));
 		}
 
 		/**
 		 * Returns attribute value, first checking it casesensitively, then caseinsensitively
 		 * If attribute is not set returns default value (if passed), or false otherwise
 		 *
 		 * @param string $name
 		 * @param mixed $default
 		 * @return string
 		 */
 		function GetAttribute($name, $default=false)
 		{
 			if (isset($this->OriginalAttributes[$name])) {
 				return $this->OriginalAttributes[$name];
 			}
 
 			return isset($this->Attributes[strtoupper($name)]) ? $this->Attributes[strtoupper($name)] : $default;
 		}
 
 		function SetParent(&$elem)
 		{
 			$this->Parent =& $elem;
 		}
 
 		/**
 		 * Adds new child to current node
 		 *
 		 * @param kXMLNode $a_child
 		 */
 		function AddChild(&$a_child)
 		{
 			$node_count = count($this->Children);
 			$a_child->Position = $node_count;
 
 			if ($node_count == 0) {
 				$this->firstChild =& $a_child;
 				$this->lastChild =& $a_child;
 			}
 			else {
 				$this->lastChild =& $a_child;
 			}
 
 			$this->Children[] =& $a_child;
 			$a_child->SetParent($this);
 		}
 
 		/**
 		 * Appends data to current node
 		 *
 		 * @param string $data
 		 */
 		function AppendData($data)
 		{
 			$this->Data .= $data;
 		}
 
 		/**
 		 * Returns child node by given path
 		 *
 		 * @param string $path
 		 * @return kXMLNode
 		 */
 		function &GetChild($path)
 		{
 			$entries = explode('/', strtoupper($path));
 			$cur = array_shift($entries);
 			if ($cur == $this->Name) $cur = array_shift($entries);
 			if (!$cur) return $this;
 			if (!isset($this->Children[$cur])) return false;
 			$left = implode('/', $entries);
 			if (!$left) return $this->Children[$cur];
 			return $this->Children[$cur]->GetChild($left);
 		}
 
 		function &GetFirstChild()
 		{
 			return $this->firstChild;
 		}
 
 		/**
 		 * Returns node value by given path
 		 *
 		 * @param string $path
 		 * @return string
 		 */
 		function GetChildValue($path)
 		{
 			$child =& $this->GetChild($path);
-			if ($child !== false) {
-				return $child->Data;
-			}
+
+			return $child !== false ? $child->Data : '';
 		}
 
 		/**
 		 * Returns child node by given position among it siblings
 		 *
 		 * @param int $position
 		 * @return kXMLNode
 		 */
 		function &GetChildByPosition($position)
 		{
 			if ($position < count($this->Children) ) {
 				return $this->Children[$position];
 			}
 			else {
 				$false = false;
 				return $false;
 			}
 		}
 
 		/**
 		 * Recursively searches for child with given name under current node
 		 *
 		 * @param string $name
 		 * @return kXMLNode
 		 */
 		function &FindChild($name)
 		{
 			$name = strtoupper($name);
-			if ($this->Name == $name) return $this;
-	//		if (isset($this->Children[$name])) return $this->Children[$name];
-	//		$children = array_keys($this->Children);
-			foreach ($this->Children as $elem)
-			{
+			if ( $this->Name == $name ) {
+				return $this;
+			}
+
+			/*if ( isset($this->Children[$name]) ) {
+				return $this->Children[$name];
+			}
+
+			$children = array_keys($this->Children);*/
+
+			foreach ($this->Children as $elem) {
+				/* @var $elem kXMLNode */
+
 				$child =& $elem->FindChild($name);
-				if ($child !== false)
-				{
+				if ( $child !== false ) {
 					return $child;
 				}
 			}
-			if (isset($child) && is_object($child)) {
+			if ( isset($child) && is_object($child) ) {
 				$child->_destruct();
 			}
+
 			unset($child);
 			$false = false;
 			return $false;
 		}
 
 		/**
-		 * Retruns value of given child or value of it's attribute
+		 * Returns value of given child or value of it's attribute
 		 *
 		 * @param string $name
 		 * @param string $attr
 		 * @return string
+		 * @access public
 		 */
-		function FindChildValue($name, $attr=null)
+		public function FindChildValue($name, $attr = null)
 		{
 			$child =& $this->FindChild($name);
 
-			if ($child !== false) {
-				if (isset($attr)) {
-					return $child->Attributes[strtoupper($attr)];
+			if ( $child !== false ) {
+				if ( isset($attr) ) {
+					return $child->Attributes[ strtoupper($attr) ];
 				}
 
 				return $child->Data;
 			}
+
+			return '';
 		}
 
 		/**
 		 * Returns next node to this, false in case of end list
 		 *
 		 * @return kXMLNode
 		 */
 		function &PrevSibling()
 		{
 			if (!is_null($this->Parent) && $this->Position > 0) {
 				$pos = $this->Position - 1;
 				do {
 					$ret =& $this->Parent->GetChildByPosition($pos--);
 				} while ($ret->Name == '_TEXT_' && $pos >= 0);
 				if ($ret->Name == '_TEXT_') $ret = false;
 				return $ret;
 			}
 			else {
 				$false = false;
 				return $false;
 			}
 		}
 
 		/**
 		 * Returns next node to this, false in case of end list
 		 *
 		 * @return kXMLNode
 		 */
 		function &NextSibling()
 		{
 			if (!is_null($this->Parent)) {
 				$pos = $this->Position + 1;
 				do {
 					$ret =& $this->Parent->GetChildByPosition($pos++);
 				} while ($pos < count($this->Parent->Children) && ($ret->Name == '_TEXT_'));
 
 				if (is_object($ret) && ($ret->Name == '_TEXT_')) {
 					$ret = false;
 				}
 				return $ret;
 			}
 			else {
 				$false = false;
 				return $false;
 			}
 		}
 		/**
 		 * Reconstructs XML of the node and subnodes
 		 *
 		 * $param bool $content_only
 		 */
 		function GetXML($content_only = false)
 		{
 			$xml = '';
 			$single = (!$this->Data && count($this->Children) == 0);
 
 			if (!$content_only) {
 				$xml = '<'.$this->OriginalName;
 
 				if (count($this->OriginalAttributes)) {
 					$xml .= ' ';
 					$att_contents = array();
 					foreach ($this->OriginalAttributes as $name => $value) {
 						$att_contents[] = $name.'="'.htmlspecialchars($value).'"';
 					}
 					$xml .= implode(' ', $att_contents);
 				}
 
 				$xml .= $single ? '/>' : '>';
 			}
 
 			if (!$single) {
 				if ($content_only) {
 					$xml .= $this->Data;
 				}
 				else {
 					$xml .= preg_match('/&|</', $this->Data) ? '<![CDATA['.$this->Data.']]>' : $this->Data;
 				}
 
 				foreach ($this->Children as $node) {
 					/* @var $node kXMLNode */
 
 					$xml .= $node->GetXML($node->Name == '_TEXT_' ? true : false);
 				}
 
 				if (!$content_only) {
 					$xml .= '</'.$this->OriginalName.'>';
 				}
 			}
 
 			return $xml;
 		}
 
 		function RemoveChild($name)
 		{
 			$child =& $this->FindChild($name);
 			$parent =& $child->Parent;
 			$pos = $child->Position;
 			array_splice($parent->Children, $pos, 1);
 			for ($i=$pos; $i < count($parent->Children); $i++) {
 				$parent->Children[$i]->Position = $i;
 			}
 			$parent->firstChild =& $parent->Children[0];
 			$parent->lastChild =& $parent->Children[count($parent->Children)-1];
 		}
 
 		function ReplaceChild($name, &$replacement)
 		{
 			$child =& $this->FindChild($name);
 			$parent =& $child->Parent;
 			$pos = $child->Position;
 			array_splice($parent->Children, $pos, 1, array($replacement));
 			$replacement->Parent =& $parent;
 			$replacement->Position = $pos;
 			$parent->firstChild =& $parent->Children[0];
 			$parent->lastChild =& $parent->Children[count($parent->Children)-1];
 		}
 
 		function SetName($name)
 		{
 			$this->Name = strtoupper($name);
 			$this->OriginalName = $name;
 		}
 
 		function SetData($data)
 		{
 			$this->Data = $data;
 		}
 
 		function SetAttribute($name, $value)
 		{
 			$this->Attributes[strtoupper($name)] = $value;
 			$this->OriginalAttributes[$name] = $value;
 		}
 	}
\ No newline at end of file