<?php
/*************************************************************************/
/* SPAGHETTILEARNING - E-Learning System                                 */
/* ============================================                          */
/*                                                                       */
/* Copyright (c) 2004 by Emanuele Sandri (esandri@tiscali.it)  			 */
/*																		 */
/* This program is free software. You can redistribute it and/or modify  */
/* it under the terms of the GNU General Public License as published by  */
/* the Free Software Foundation; either version 2 of the License.        */
/*************************************************************************/
/**
 * @module CPManager.php
 *
 * This module is the content package manager of the SpaghettiScorm
 *
 * @author Emanuele Sandri
 * @version $Id: CPManager.php 113 2006-03-08 18:08:42Z ema $
 * @copyright 2004
 **/

include_once( dirname(__FILE__).'/scorm_utils.php');


/**
 * @class CPManager
 **/
class CPManager {
	var $path = '';
	var $id = '';
	var $identifier = '';

	var $errCode = 0;
 	var $errText = '';

	var $imsManifestDom;
	var $defaultOrg;
	var $orgElems;
	var $resElems;

	function Open( $path ) {
		$this->path = $path;
		return TRUE;
	}

	/**
	 * Return the content of the file imsmanifest.xml
	 * @return content of the imsmanifest.xml file as a string or FALSE
	 *  it there is any error.
	 */
	function ReadImsmanifest() {
		$filename = $this->path . '/imsmanifest.xml';
		$handle = @fopen($filename, 'r');
		if( $handle === FALSE ) {
		    $this->setError( SPSCORM_E_FILENOTFOND, 'File: '. $this->path. '/imsmanifest.xml not found' );
		    return FALSE;
		}
		$contents = fread($handle, filesize($filename));
		fclose($handle);
		return $contents;
	}
	
	/** Parse the imsmanifest.xml file contained in Content Package
	 *  @return TRUE if success, FALSE otherwise; use GetLastError to get the
	 *  last generated error
	 */
	function ParseManifest() {
		// Create an DomDocument object from XML file
		
		// first read Imsmanifest in a string
		$strImsmanifest = $this->ReadImsmanifest();
		if( $strImsmanifest === FALSE )
		    return FALSE;
		// the create xml document from memory
		$this->dom =& DoceboDOMDocument::loadXML($strImsmanifest);
		if($this->dom === NULL ) {
        	$this->setError( SPSCORM_E_INVALIDMANIFEST, 'The imsmanifest of '. $this->path .' is not valid' );
			return FALSE;
		}

		$root = $this->dom->documentElement;
		$this->identifier = $root->getAttribute('identifier');
		
		// now get all the organizations
		// to avoid problem with differents behaviors in different versions
		// of PHP we get organization tag elements starting from
		// direct parent organizations.
        $organizations = getelementbynameattrib($root, 'organizations');
		$this->orgElems = $organizations->getElementsByTagName( 'organization' );
		
		$this->defaultOrg = $organizations->getAttribute('default');
		if( $this->defaultOrg == '' ) {
			if( $this->orgElems->length > 0 ) {
				$defOrg = $this->orgElems->item(0);
				$this->defaultOrg = $defOrg->getAttribute('identifier');
			} else
				$this->defaultOrg = '-resource-';
		}

		// and now get the reference to the resources element
		$resources = getelementbynameattrib($root, 'resources' );
		$this->resElems = $resources->getElementsByTagName( 'resource' );
		
		return TRUE;
	}
	
	/** This method return an organization based on their identifier
	 *  @param $identifier the identifier of the organization to be returned
	 *      if this parameter is an empty string the returned organization is
	 *      the default.
	 *  @return the requested organization, or the default organization if
	 *      the identifier param has not defined, or NULL if no organization
	 *      was found with passed identifier.
     */
	function GetOrganization( $identifier ) {
		for( $i = 0; $i < $this->orgElems->length; $i++ ) {
			$org_elem =& $this->orgElems->item($i);
			if( $org_elem->getAttribute('identifier') == $identifier )
			    return $org_elem;
		}
		return NULL;
	}

	/** This method return a resource based on their identifier
	 *  @param $identifier the identifier of the reource to be returned
	 *  @return the requested reource, or NULL if no resource
	 *      was found with passed identifier.
     */
	function GetResource( $identifier ) {
		for( $i = 0; $i < $this->resElems->length; $i++ ) {
			$res =& $this->resElems->item($i);
			if( $res->getAttribute('identifier') == $identifier ) {
			    return $res;
			}
		}
		return NULL;
	}
	
	function GetResourceInfo( $identifier ) {
		$res = $this->GetResource( $identifier );
		if( !$res ) {
			echo "<!-- Resource $identifier not found. \n";
			print_r($this->resElems);
			echo " -->\n";
			return;
		}
		/*$attriblist = $res->attributes();
		foreach( $attriblist as $key => $a ) 
			$info[$key] = $a;*/
		$info['href'] = $res->getAttribute('href');
		if( $info['href'] == '' ) {
			$filetag = getelementbynameattrib($res, 'file' );
			if( $filetag !== NULL ) {
				$info['href'] = $res->getAttribute('base').$filetag->getAttribute('href');
			}
		}
		$info['identifier'] = $res->getAttribute('identifier');
		$info['type'] = $res->getAttribute('type');
		$info['scormtype'] = $res->getAttribute('scormtype');
		return $info;
	}
	
	function GetResourceNumber() {
		return $this->resElems->length;
	}
	
	function GetResourceIdentifier( $nRes ) {
		$res = $this->resElems->item($nRes);
		return $res->getAttribute('identifier');
	}
	/**
	 * This function render the organization identified by $identifier using
	 *  the $renderer object; this must be inherited from RendererAbstract
	 *  and must implement all the functions declared in this class
	 *  @param $identifier idendifier of the organization to render
	 *  @param &$renderer object that render the organization
	 */
	function RenderOrganization( $identifier, &$renderer) {
		if( $identifier == '' || $identifier == '-resource-' ) {	// no organization => resources
			return $this->RenderResourceOrganization( $renderer); 
		} 
		$organization = $this->GetOrganization( $identifier );
		$itemInfo['identifier'] = $identifier;
		$itemInfo['isLast'] = TRUE;     // the organization is always the last
        $itemInfo['identifierref'] = FALSE;
        $itemInfo['isvisible'] = TRUE;
        $itemInfo['parameters'] = FALSE;
        
		$nodeTitle = $this->getFirstElementNode($organization, 'title');
		$title = $nodeTitle->firstChild;
		$itemInfo['title'] = $title->textContent;
        
		$elem = $this->getFirstElementNode( $organization, 'item' );
        if( $elem )
			$itemInfo['isLeaf'] = FALSE;
		else
		    $itemInfo['isLeaf'] = TRUE;

        $renderer->RenderStartItem( $this, $itemInfo );
        
		while( $elem ) {
			if( ($elem->nodeType == XML_ELEMENT_NODE) && ($elem->nodeName == "item") ) {
				$this->RenderItem( $elem, $renderer, true );
			}
			$elem = $elem->nextSibling;
		}
		
		$renderer->RenderStopItem( $this, $itemInfo );
		
	}
	
	function RenderItem( &$item, &$renderer, $isLast ) {
		// collect infos about item
		$itemInfo['identifier'] = $item->getAttribute('identifier');
		
		$itemInfo['isLast'] = $isLast;
		
		if( $item->hasAttribute('identifierref') )
			$itemInfo['identifierref'] = $item->getAttribute('identifierref');
		else
		    $itemInfo['identifierref'] = FALSE;

		if( $item->hasAttribute('isvisible') )
		    $itemInfo['isvisible'] = ($item->getAttribute('isvisible')=='true')?TRUE:FALSE;
		else
		    $itemInfo['isvisible'] = TRUE;

		if( $item->hasAttribute('parameters') )
		    $itemInfo['parameters'] = $item->getAttribute('parameters');
		else
		    $itemInfo['parameters'] = FALSE;
		
		$itemInfo['title'] = $this->getFirstElementValue($item, 'title');
		$itemInfo['adlcp_prerequisites'] = $this->getFirstElementValue($item, 'adlcp:prerequisites');
		$itemInfo['adlcp_maxtimeallowed'] = $this->getFirstElementValue($item, 'adlcp:maxtimeallowed');
		$itemInfo['adlcp_timelimitaction'] = $this->getFirstElementValue($item, 'adlcp:timelimitaction');
		$itemInfo['adlcp_datafromlms'] = $this->getFirstElementValue($item, 'adlcp:datafromlms');
		$itemInfo['adlcp_masteryscore'] = $this->getFirstElementValue($item, 'adlcp:masteryscore');
				
        $elem = $this->getFirstElementNode( $item, 'item');
        if( $elem )
			$itemInfo['isLeaf'] = FALSE;
		else
		    $itemInfo['isLeaf'] = TRUE;
		    
		if( $renderer == null ) {
			print_r( $itemInfo );
			
		}
		$renderer->RenderStartItem(	$this, $itemInfo );
		
		while( $elem ) {
			$nextElem = $this->getNextElementNode( $elem );
			
			/* pass the info about the last element */
			if( $nextElem === NULL )
			    $this->RenderItem( $elem, $renderer, true );
			else
			    $this->RenderItem( $elem, $renderer, false );
			    
			$elem = $nextElem;
		}

        $renderer->RenderStopItem( $this, $itemInfo );

	}

	function RenderResourceOrganization( &$renderer) {
		$itemInfo['identifier'] = '-resource-';
		$itemInfo['isLast'] = TRUE;     // the organization is always the last
        $itemInfo['identifierref'] = FALSE;
        $itemInfo['isvisible'] = TRUE;
        $itemInfo['parameters'] = FALSE;
        
		$itemInfo['title'] = 'Lesson resources';
		$resNum = $this->GetResourceNumber();
        $itemInfo['isLeaf'] = ($resNum == 0);

        $renderer->RenderStartItem( $this, $itemInfo );
		
		for( $i = 0; $i < $resNum; $i++ ) {
			$resInfo = $this->GetResourceInfo( $this->GetResourceIdentifier($i) );
			
			// href, identifier, type, scormtype
			$resInfo['isLast'] = ($i == ($resNum-1));
			$resInfo['identifierref'] = $resInfo['identifier'];
		    $resInfo['isvisible'] = TRUE;
			$resInfo['parameters'] = FALSE;
			$resInfo['title'] = $resInfo['identifier'];
			$resInfo['adlcp_prerequisites'] = '';
			$resInfo['adlcp_maxtimeallowed'] = '';
			$resInfo['adlcp_timelimitaction'] = '';
			$resInfo['adlcp_datafromlms'] = '';
			$resInfo['adlcp_masteryscore'] = '';
			$resInfo['isLeaf'] = TRUE;
				
			$renderer->RenderStartItem(	$this, $resInfo );
			$renderer->RenderStopItem( $this, $resInfo );
			
		}
		
		$renderer->RenderStopItem( $this, $itemInfo );
		
	}
	
	/**
	 * Return the first element in the next deep level
	 *	of that passed and with tag equal to $tagname
	 *  @param &$elem reference to current element
	 *  @param $tagname the tag of desired element
	 *  @return next sibling elment or NULL if not find
	 */
	function getFirstElementNode( &$elem, $tagname = '') {
		$nextElem = $elem->firstChild;

		while( $nextElem ) {
			if( ($nextElem->nodeType == XML_ELEMENT_NODE) ) { // only node elements
				if( $tagname != '' ) {
					if($nextElem->nodeName == $tagname)
						return $nextElem;
				} else {
                    return $nextElem;
				}
			}
			$nextElem = $nextElem->nextSibling;
		}
	    return NULL;
	}
	
	/**
	 * Return the next element in the same level of that passed
	 *  and with node_name equal to $nodename
	 *  @param &$elem reference to current element
	 *  @param $sameTag the element retured must have same tag of $elem
	 *  @return next sibling elment or NULL if not find
	 */
	function getNextElementNode( &$elem, $sameTag = TRUE ) {
		$nextElem =& $elem->nextSibling;
		
		while( $nextElem ) {
			if( ($nextElem->nodeType == XML_ELEMENT_NODE) ) { // only node elements
				if( $sameTag ) {
					if($nextElem->nodeName == $elem->nodeName )
						return $nextElem;
				} else {
                    return $nextElem;
				}
			}
			$nextElem = $nextElem->nextSibling;
		}
	    return NULL;
	}
	
	/**
	 * Return the value of the first element in the next deep level
	 *	of that passed and with tag equal to $tagname
	 *  @param &$elem reference to current element
	 *  @param $tagname the tag of desired element
	 *  @return next sibling elment or NULL if not find
	 */
	function getFirstElementValue( &$elem, $tagname = '') {
		$nextElem = $elem->firstChild;
		$node = NULL;

		while( $nextElem ) {
			if( ($nextElem->nodeType == XML_ELEMENT_NODE) ) { // only node elements
				if( $tagname != '' ) {
					if($nextElem->nodeName == $tagname) {
						$node = $nextElem;
						break;
					} else {
						//echo "skip ".$nextElem->nodeName."\n";
					}
				} else {
                    $node = $nextElem;
					break;
				}
			}
			$nextElem = $nextElem->nextSibling;
		}
		if( $node != NULL ) {
			//echo "node ".$tagname." found!\n";
			$elemValue = $node->firstChild;
			if( $elemValue != NULL )
				return $elemValue->nodeValue;
		} else {
			//echo "node ".$tagname." not found!\n";
		}
	    return NULL;
	}
	
	/**
	 * Set the error
	 */
	function setError( $errCode, $errText ) {
		$this->errCode = $errCode;
		$this->errText = $errText;
	}
}

?>
