<?php
define('idDiGIR_getCap','$Id: DiGIR_getCaps.php,v 1.8 2004/05/19 11:54:00 rdg Exp $');
if (defined('idDIGIR_VERSION_REQUEST'))
    return;
/*
* $Id: DiGIR_getCaps.php,v 1.8 2004/05/19 11:54:00 rdg Exp $
*/
/**
* DiGIRGetCapabilities
* returns a capabilities document for the service.
*/

require_once 'DiGIR_config.php';
require_once 'DiGIR_utils.php';
require_once DIGIR_XPATH_LIBRARY;
require_once DIGIR_ADODB_LIBRARY;


/**
* Adds a node to the metadata response document by extracting its value from
* another xml data source.
* @param XPath $xp XPath object from which a value is to be extracted.
* @param string $qry XPath query string that identifies the node to be extracted
*                    from $xp.
* @param string $nodeName The name of the node to create
* @param string $srcFile The name of the source file used for $xp - only used
*                        for indicating source of problem with value extraction
* @param boolean $showError Indicates if an error value should be shown if the
*                        $qry doesn't return any nodes.  defautls to TRUE.
* @param string $defVal Default value for element if attribute is not set.
*/
function addMetaElement(&$xp,$qry,$nodeName,$srcFile,$showError=TRUE,$defVal='')
{
    $nodes= $xp->match($qry);
    echo "<".$nodeName.">";
    if (count($nodes) > 0)
    {
        $val = $xp->getData($nodes[0]);
	if (empty($val))
        {
            //Look for value on attribute "v"
            $attrs = $xp->getAttributes($nodes[0]);
            $val = (isset($attrs['v'])) ? $attrs['v'] : $defVal;
	}
        echo $val;
        //echo $xp->getData($nodes[0]);
    }
    elseif ($showError)
    {
        echo "WARNING: no ".$nodeName." value specified in metadata";
        appendError(DC_METADATA,
            htmlEntities("No ".$qry." element in ".$srcFile),
            DIAG_WARN);
    }
    echo "</".$nodeName.">\n";
}

/**
* Adds a node to the metadata response document by extracting its value from
* an attribute of another xml data source.
* @param XPath $xp XPath object from which a value is to be extracted.
* @param string $qry XPath query string that identifies the node to be extracted
*                    from $xp.
* @param string $attrName The name of the attribute that contains the desired value
* @param string $defVal Default value for element if attribute is not set.
* @param string $nodeName The name of the node to create
* @param string $srcFile The name of the source file used for $xp - only used
*                        for indicating source of problem with value extraction
* @param boolean $showError Indicates if an error value should be shown if the
*                        $qry doesn't return any nodes.  defautls to TRUE.
*/
function addMetaElementAttr(&$xp,$qry,$attrName,$defVal,$nodeName,$srcFile,$showError=TRUE)
{
    $nodes= $xp->match($qry);
    echo "<".$nodeName.">";
    if (count($nodes) > 0)
    {
        $attrs = $xp->getAttributes($nodes[0]);
        if (isset($attrs[$attrName]))
            echo $attrs[$attrName];
        else
            echo $defVal;
    }
    elseif ($showError)
    {
        echo "WARNING: no ".$nodeName." value specified in metadata";
        appendError(DC_METADATA,
            htmlEntities("No ".$qry." element in ".$srcFile),
            DIAG_WARN);
    }
    echo "</".$nodeName.">\n";
}

/**
* Generates a <contact> block for the metadata response.
*/
function outputContacts(&$xp,$contactQry,$srcFile)
{
    $nodes = $xp->match($contactQry);
    if (count($nodes) > 0)
    {
        foreach ($nodes as $node)
        {
            //get the type of contact
            $cType = $xp->getAttributes($node,'type');
            echo "<contact type='".$cType."'>\n";
            addMetaElement($xp,$node."/name","name",$srcFile,FALSE);
            addMetaElement($xp,$node."/title","title",$srcFile,FALSE);
            addMetaElement($xp,$node."/emailAddress","emailAddress",
                    $srcFile,FALSE);
            addMetaElement($xp,$node."/phone","phone",$srcFile,FALSE);
            echo "</contact>\n";
        }
    }
}

/**
* Extracts the total number of records and the data last modified from the
* database.  If the dateLastModified can not be discerned then the current
* timestamp is used.  Good reason to add a timestamp to the records!!
*
* @param string $resourceFile Full path to the resource configuration file
* @return array [0] = total number of records, [1] = youngest time stamp.
*/
function extractDatabaseInfo($resourceFile,$conceptSchema)
{
    $timeStamp = date(TIME_FORMAT_STRING, time());
    $ares = array(0,$timeStamp);

    $cfgBuilder = new ConfigBuilder($resourceFile);
    $Config = $cfgBuilder->Config;
    $cn = &ADONewConnection($Config->connectionType);

    if (!is_object($cn))
    {
        appendError(DC_INTERNALDATABASEERROR,
            'A connection to the database could not be created.',DIAG_ERROR);
        return $ares;
    }

    //Set the charPage attribute of the connection.
    //This only works for OLEDB based connections, but will not hurt
    //other types of connections
    $charPage = encodingToCodePage($Config->connectionEncoding);
    if (!is_null($charPage))
        $cn->charPage = $charPage;

    $cn->fmtDate = $cn->fmtTimeStamp;

    $res = $cn->PConnect($Config->connectionString,
                         $Config->connectionUID,
                         $Config->connectionPWD,
                         $Config->connectionDB);
    if (!$res)
    {
        appendError(DC_INTERNALDATABASEERROR,
            htmlEntities("ADOdb reported error connecting to database:".
              $cn->errorMsg()),DIAG_ERROR);
        $cn->Close();
        return $ares;
    }

    $rootTable = $Config->RootTableName();
    $rootKeyField = $Config->tableKeyFieldName($rootTable);
    $sql = "SELECT DISTINCT $rootTable.$rootKeyField";
    $sql .= $Config->FROMclause();

    $localFilter = $Config->localFilter();

    if ($localFilter)
    {
        $sql .= ' WHERE '.$localFilter;
    }

    $rs = $cn->Execute($sql);
    if (!is_object($rs))
    {
        $err = $cn->ErrorMsg();
        appendError(DC_DATABASE_ERROR,htmlentities($err),DIAG_ERROR);
        appendError(DC_DEBUG_SQL,htmlentities($sql),DIAG_DEBUG);
        $cn->Close();
        return $ares;
    }
    if (!$rs->EOF)
    {
        $ares[0] = $rs->RecordCount();
    }
    $rs->Close();
    
    //So how do we find the "dateLastModified" field to examine?
    //For now let's just use the concept called "DateLastModfied" with the
    //same name space as indicated in the configuration metadata
    
    $cid = $Config->getConceptByName("DateLastModified",$conceptSchema);
    if ($cid >= 0)
    {
        $concept = $Config->getConcept($cid);
        $CTable = $concept[1];
        $CField = $concept[2];
        if ($CTable && $CField)
        {
            $sql = 'SELECT MAX('.$CTable.'.'.$CField.') ';
            $sql .= $Config->FROMclause();

            $localFilter = $Config->localFilter();

            if ($localFilter)
            {
                $sql .= ' WHERE '.$localFilter;
            }

            $rs = $cn->Execute($sql);
            if (!is_object($rs))
            {
                $err = $cn->ErrorMsg();
                appendError(DC_DATABASE_ERROR,htmlentities($err),DIAG_ERROR);
                appendError(DC_DEBUG_SQL,htmlentities($sql),DIAG_DEBUG);
                $cn->Close();
                return $ares;
            }
            if (!$rs->EOF)
            {
                $ares[1] = $rs->fields[0];
            }
            $rs->Close();
        }
    }
    $cn->Close();
    return $ares;
}

/**
* Generates the metadata response from the configuration
* files.  The output from this routine should be cached as it
* is a relatively expensive process.
*/
function getCapabilities2($params)
{
    echo "<content>\n";
    if (_DEBUG)
    {
        echo "<d>\n";
        print_r($params);
        echo "</d>";
    }
    $implementationID = DiGIRVERSION;
    $endPoint = serviceID();
    $hostMetadataFile = realpath(DIGIR_CONFIG_DIR.'/'.DIGIR_METADATA_FILE);
    $resourcesFile = realpath(DIGIR_CONFIG_DIR.'/'.DIGIR_RESOURCE_FILE);

    if (strlen($params['clientkey']) > 0)
    {
        echo "<metadata clientKey='".$params['clientkey']."'>\n<provider>\n";
    }
    else
        echo "<metadata>\n<provider>\n";

    $xp = new XPath();
	$xp->setVerbose(0);
	$xp->setXmlOption(XML_OPTION_CASE_FOLDING,false);
	$xp->setXmlOption(XML_OPTION_SKIP_WHITE,true);
    if (!$xp->importFromFile($hostMetadataFile))
    {
        appendError(DC_METADATA,
                "Bad metadata configuration - check location of metadata file (".
                $hostMetadataFile.")",
                DIAG_ERROR);
        echo "</provider></metadata>";
    	echo "</content>\n";
	    //echo the errors here so the output is captured by the caching mechanism.
	    echo DiGIRErrors($params['clientkey']);	
	    return;
    }
    //send the system metadata
    addMetaElement($xp,"//metadata/name","name",$hostMetadataFile);
    echo "<accessPoint>".htmlEntities($endPoint)."</accessPoint>\n";
    echo "<implementation>".$implementationID."</implementation>\n";
    echo "<host>";
    addMetaElement($xp,"//metadata/host/name","name",$hostMetadataFile);
    addMetaElement($xp,"//metadata/host/code","code",$hostMetadataFile);
    addMetaElement($xp,"//metadata/host/relatedInformation",
        "relatedInformation",$hostMetadataFile);
    //host contact information
    outputContacts($xp,"//metadata/host/contact",$hostMetadataFile);
    addMetaElement($xp,"//metadata/host/abstract","abstract",$hostMetadataFile);
    echo "</host>";
    //$xp = NULL;

    //get the resources from the resources.xml file
    $xp2 = new XPath();
	$xp2->setVerbose(0);
	$xp2->setXmlOption(XML_OPTION_CASE_FOLDING,false);
	$xp2->setXmlOption(XML_OPTION_SKIP_WHITE,true);
    $resa = getResourceList($resourcesFile);
    foreach ($resa as $resName => $resFile)
    {
    	$cPath = realpath(DIGIR_CONFIG_DIR.'/'.$resFile);
		$cName = $resName;
        if (strlen($cName) > 0)
        {
            $conceptSchema = '';
            $xp2->reset();
            if ($xp2->importFromFile($cPath))
            {
                echo "<resource>\n";
                addMetaElement($xp2,"//configuration/metadata/name","name",
                    $cPath);
                echo "<code>".$cName."</code>\n";
                addMetaElement($xp2,"//configuration/metadata/relatedInformation",
                                "relatedInformation",
                                $cPath);

                //contact(s)
                outputContacts($xp2,"//configuration/metadata/contact",$cPath);

                //other resource info
                addMetaElement($xp2,"//configuration/metadata/abstract",
                    "abstract",
                    $cPath);
                addMetaElement($xp2,"//configuration/metadata/keywords",
                    "keywords",
                    $cPath);
                addMetaElement($xp2,"//configuration/metadata/citation",
                    "citation",
                    $cPath);
                addMetaElement($xp2,"//configuration/metadata/useRestrictions",
                    "useRestrictions",
                    $cPath);
                $cSchemas = $xp2->match("//configuration/metadata/conceptualSchema");
                if (count($cSchemas) > 0)
                {
                    foreach ($cSchemas as $xpath)
                    {
                        echo "<conceptualSchema schemaLocation='";
                        echo $xp2->getAttributes($xpath,'schemaLocation');
                        echo "'>";
                        $conceptSchema = trim($xp2->getData($xpath));
                        echo htmlEntities($conceptSchema);
                        echo "</conceptualSchema>\n";
                    }
                }
                addMetaElement($xp2,"//configuration/metadata/recordIdentifier",
                    "recordIdentifier",
                    $cPath);
                addMetaElement($xp2,"//configuration/metadata/recordBasis",
                    "recordBasis",
                    $cPath);

                $ares = extractDatabaseInfo($cPath,$conceptSchema);
                echo "<numberOfRecords>".$ares[0]."</numberOfRecords>\n";
                echo "<dateLastUpdated>".$ares[1]."</dateLastUpdated>\n";
                
                addMetaElement($xp2,'//configuration/metadata/minQueryTermLength',
                    'minQueryTermLength',$cPath,TRUE,g_MinTextLength);
                addMetaElement($xp2,'//configuration/metadata/maxSearchResponseRecords',
                    'maxSearchResponseRecords',$cPath,TRUE,DIGIR_ABS_RECORD_LIMIT);
                addMetaElement($xp2,'//configuration/metadata/maxInventoryResponseRecords',
                    'maxInventoryResponseRecords',$cPath,TRUE,DIGIR_ABS_RECORD_LIMIT);
                echo "</resource>\n";
            }
            else
            {
                appendError(DC_METADATA,
                  htmlEntities("Could not open the resource file (".
                    $cPath.") identified by the resource name \"".
                    $cName."\" in the resources file: ".$resourceFile),
                    DIAG_WARN);
            }
            //$xp2 = NULL;
        }
        else
        {
            appendError(DC_METADATA,
                htmlEntities("Problem in ".$resourcesFile.".  <resource> element
                specified but name attribute missing or blank."),
                DIAG_WARN);
        }
    }
    echo "</provider>\n</metadata>\n";
	echo "</content>\n";
	//echo the errors here so the output is captured by the caching mechanism.
	echo DiGIRErrors($params['clientkey']);	
}

//test block - executed if script called directly
if ((isset($_SERVER['SCRIPT_NAME']) && preg_match("/.\/DiGIR_getCaps.php/i",$_SERVER['SCRIPT_NAME'])) ||
    (isset($_SERVER['argv'][0]) && strcasecmp($_SERVER['argv'][0],'DiGIR_getCaps.php') === 0))
{
	require_once('phpUnit.php');
	class getCapsTest extends PHPUnit_TestCase 
	{
		var $cnType;
		
		function getCapsTest($name) 
		{
			$this->PHPUnit_TestCase($name);
		}
 
		function setUp() 
		{
		    require_once('adodb/adodb.inc.php');
			$this->cnType = 'ado_access';
			
			$this->cnStr = 'Provider=Microsoft.JET.OLEDB.4.0;Data Source="d:\digir\database\test.mdb"';
			$this->uid = "";
			$this->pwd = "";
			$this->database = "";
		}
		
		function assertRange($v,$minv,$maxv,$msg)
		{
			$res = array(TRUE,'');
			if ($v < $minv)
			{
				$res[0] = FALSE;
				$res[1] = sprintf($msg,'low ('.$v.')');
			}	
			if ($v > $maxv)
			{
				$res[0] = FALSE;
				$res[1] = sprintf($msg,'high ('.$v.')');
			}	
			return res;
		}
		
		function test_DBDriver()
		{
		    $cn = &ADONewConnection($this->cnType);
		    $this->assertTrue(is_object($cn),
		    	'Failed to connect to database driver for driver type = '.$this->cnType);
		    $cn = NULL;
		}
		
		function test_DBConnect()
		{
		    $cn = &ADONewConnection($this->cnType);
		    $this->assertTrue(is_object($cn),
		    	'Failed to connect to database driver for driver type = '.$this->cnType);
		    $cn = NULL;
		}
	}

    echo realpath(DIGIR_CONFIG_DIR.'/'.DIGIR_METADATA_FILE);
    echo realpath(DIGIR_CONFIG_DIR.'/'.DIGIR_RESOURCE_FILE);
	
	$suite = new PHPUnit_TestSuite('getCapsTest');
	$result = PHPUnit::run($suite);
	echo $result->toHTML();
}
?>
