<?php
/**

setup.php - A web configurator to the PHP DiGIR provider implementation.
---------
see: http://www.sf.net/projects/digir

LICENSE INFORMATION
-------------------
Copyright(c) 2003 by CRIA - Centro de Referencia em Informacao Ambiental
http://www.cria.org.br

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, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details:

http://www.gnu.org/copyleft/gpl.html


Main author: Renato De Giovanni <renato [at] cria.org.br>
-----------

**/

# Debugging tip: install the "apd" pear package and uncomment the line below
#apd_set_pprof_trace();
#error_reporting (E_ALL);

session_start();

$ctype = (isset($_REQUEST['get_xml'])) ? 'xml' : 'html';

header('Content-Type: text/'.$ctype.'; charset="UTF-8"');
header('Last-Modified: ' . gmdate("D, d M Y H:i:s") . ' GMT');
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Cache-Control: no-cache, no-store, post-check=0, pre-check=0');
header('Pragma: no-cache');

# Script name
define('PHP_SELF', $_SERVER['PHP_SELF']);

# Default XML header
define('XML_HEADER', '<?xml version="1.0" encoding="utf-8" ?>');

// should be included first
require_once('../www/DiGIR_globals.php');

//dv: update the include path to point to the digir code base
ini_set('include_path',ini_get('include_path').DIGIR_PATH_SEP.realpath('../www/'));

//remainder should be on the path now.
require_once(DIGIR_XPATH_LIBRARY);
require_once(DIGIR_ADODB_LIBRARY);
require_once('Cache.php'); # pear library
require_once('setup_lib.php');
require_once('xmlForm.inc.php');

# No magic quotes, please...
set_magic_quotes_runtime(0);

if (get_magic_quotes_gpc())
{
  stripMagicSlashes($_POST);
  stripMagicSlashes($_GET);
  stripMagicSlashes($_COOKIES);
  stripMagicSlashes($_REQUEST);
}

$providerFile  = realpath(DIGIR_CONFIG_DIR.'/'.DIGIR_METADATA_FILE);
# since "realpath" returns an empty string if file does not exist:
$providerFile = ($providerFile) ? $providerFile : DIGIR_CONFIG_DIR.'/'.DIGIR_METADATA_FILE;

$resourcesFile = realpath(DIGIR_CONFIG_DIR.'/'.DIGIR_RESOURCE_FILE);
$resourcesFile = ($resourcesFile) ? $resourcesFile : DIGIR_CONFIG_DIR.'/'.DIGIR_RESOURCE_FILE;

$schemaFile = realpath(DIGIR_CONFIG_DIR.'/'.'digir.xsd');

clearstatcache(); # clear php cache for file function calls

$errStr = '';
$msg    = '';

# Since "fopen" is used many times to open remote files, two minutes seems to much
# to wait for a response from eventually wrong URLs. 30 seconds looks better:
if (version_compare(phpversion(), '4.3.0', '>=') > 0)
{
  ini_set('default_socket_timeout', 30);
}

# Some common messages
$savedChangesMsg    = 'Changes were successfully saved!';
$warnAboutSavingMsg = "Notice: clicking on add/remove buttons does not save any changes!\nTo save something, use the corresponding button in the end of the page.";

# Some common variables
$pathToSchemaVersion = '/xsd:schema[1]/xsd:annotation[1]/xsd:documentation[1]';
# note: it is necessary to split the two values below into more than one string, otherwise
#       cvs recognizes it as a cvs id and changes its content to the current version of this file!!
$schemaVersionExample = '$'.'Id: digir.xsd,v 1.6 2003/06/13 16:47:16 rdg Exp $';
$schemaVersionRegexp = '/^\$'.'Id:\sdigir\.xsd,v\s([\S]+)\s(\d{4}\/\d{2}\/\d{2}\s\d{2}:\d{2}:\d{2})\s([\S]+)\sExp\s\$$/';

$cacheOptions = array('cache_dir' => DIGIR_CACHE_DIRECTORY);
$cacheExpires = 94608000; # (3 years)

#unset($_SESSION['setup']); # used to debug setup

###############    Check some basic conditions before proceeding    ################
####################################################################################

if (!isset($_SESSION['setup']))
{
  $msgs = array();

  # Check PHP version
  if (version_compare(phpversion(), DIGIR_MIN_PHP_VERSION, '<') > 0)
    {
      array_push($msgs, 
		 'PHP Version '.DIGIR_MIN_PHP_VERSION.' or later is required to run a provider '.
		 '(detected version '.phpversion().').');
    }

  # Check mbstring
  if (!loadlibrary_x('mbstring'))
    {
      array_push($msgs, 'Could not load the PHP "mbstring" extension, please check your PHP configuration before proceeding...');
    }

  # Check Session control
  $_SESSION['test'] = '1';
  session_write_close();
  session_start();
  if (!isset($_SESSION['test']))
    {
      array_push($msgs, 'PHP session control is not working properly (check in your PHP configuration if session support is enabled, and also if session.save_path exists and is writable by the webserver)');
    }

  # Provider configuration file
  if (!file_exists($providerFile))
    {
      $templateXml = '<metadata>'.
                       '<accessPoint>! set by application !</accessPoint>'.
                       '<implementation>! set by application !</implementation>'.
                       '<name>My Provider</name>'.
                     '</metadata>';

      $xpp = new XPath();
      $xpp->setVerbose(0);
      $xpp->setXmlOption(XML_OPTION_CASE_FOLDING,false);
      $xpp->setXmlOption(XML_OPTION_SKIP_WHITE,true);
      $xpp->importFromString($templateXml);

      # Try to save new file
      if (!$xpp->exportToFile($providerFile, '', XML_HEADER))
	{
	  $msgStr = 'Could not create provider xml configuration file ('.$providerFile.").\n". 
	    $xpp->getLastError();

	  array_push($msgs, $msgStr);
	}
      else
	{
	  $_REQUEST['show_provider_form'] = 1;

	  $msg = 'A new provider xml configuration file has been created in the following place:'.
                 "\n\n".$providerFile."\n\n".
                 'Please, complete the form below and save your changes before adding resources.'.
                 "\n\n".$warnAboutSavingMsg;
	}
    }
  elseif (!is_writable($providerFile))
    {
      array_push($msgs, 'The provider xml configuration file ('.$providerFile.') defined with DIGIR_METADATA_FILE by localconfig.php should be writable by the webserver!');
    }

  # Resources configuration file
  if (!file_exists($resourcesFile))
    {
      $templateXml = '<resources></resources>';

      $xpl = new XPath();
      $xpl->setVerbose(0);
      $xpl->setXmlOption(XML_OPTION_CASE_FOLDING,false);
      $xpl->setXmlOption(XML_OPTION_SKIP_WHITE,true);
      $xpl->importFromString($templateXml);

      # Try to save new file
      if (!$xpl->exportToFile($resourcesFile, '', XML_HEADER))
	{
	  $msgStr = 'Could not create provider xml configuration file ('.$resourcesFile.").\n". 
	    $xpl->getLastError();

	  array_push($msgs, $msgStr);
	}
    }
  elseif (!is_writable($resourcesFile))
    {
      array_push($msgs, 'The resources list xml configuration file ('.$resourcesFile.') defined with DIGIR_RESOURCE_FILE by localconfig.php should be writable by the webserver!');
    }

  # Prepare schema file parser 
  $xps = new XPath();
  $xps->setVerbose(0);
  $xps->setXmlOption(XML_OPTION_CASE_FOLDING,false);
  $xps->setXmlOption(XML_OPTION_SKIP_WHITE,true);

  $loadedSchema = 1;
  $copiedFromRemoteSchema = 0;

  $cache = new Cache('file', $cacheOptions);
  $cacheId = $cache->generateID('remoteSchemaLocation');

  $remoteSchemaLocation = 'http://digir.net/schema/protocol/2003/1.0/digir.xsd';

  if ($cachedData = $cache->get($cacheId, 'configurator'))
    {
      $remoteSchemaLocation = $cachedData;
    }
  else
    {
      $cache->save($cacheId, $remoteSchemaLocation, 0, 'configurator');
    }

  $_SESSION['remoteVersion'] = array('location'    => $remoteSchemaLocation,
				     'number'      => '?',
				     'lastchanged' => '?',
				     'responsible' => '?');
  
  # Local copy of protocol schema
  if (!file_exists($schemaFile))
    {
      # Try to copy it from a default location
      if (!$xps->importFromFile($remoteSchemaLocation) or
	  !$xps->exportToFile($schemaFile))
	{
	  array_push($msgs, 'Could not create a local copy of DiGIR xml schema from its default remote location (<a href="'.$remoteSchemaLocation.'" target="_new">'.$remoteSchemaLocation.'</a>): '.
		     $xps->getLastError());

	  $loadedSchema = 0;
	}

      $copiedFromRemoteSchema = 1;
    }
  else
    {
      $loadedSchema = $xps->importFromFile($schemaFile);
    }

  # Get version being used
  if ($loadedSchema and $xps->match($pathToSchemaVersion))
    {
      $rawVersionContent = $xps->getData($pathToSchemaVersion);

      # See content of $schemaVersionExample above
      if (preg_match($schemaVersionRegexp, $rawVersionContent, $matches))
	{
	  $_SESSION['localVersion'] = array('number'      => $matches[1],
					    'lastchanged' => $matches[2],
					    'responsible' => $matches[3]);
	}
    }

  if ($loadedSchema and !isset($_SESSION['localVersion']))
    {
      array_push($msgs, 'Could not determine the DiGIR protocol version being used. '.
		 'Does it have an xsd:documentation element with this pattern? <br>'.
		 $schemaVersionExample);
    }

  # Get data from remote protocol schema
  $loadedRemoteSchema = 1;

  if ($copiedFromRemoteSchema)
    {
      $xpm =& $xps;
    }
  else
    {
      $xpm = new XPath();
      $xpm->setVerbose(0);
      $xpm->setXmlOption(XML_OPTION_CASE_FOLDING,false);
      $xpm->setXmlOption(XML_OPTION_SKIP_WHITE,true);

      if (!$xpm->importFromFile($remoteSchemaLocation))
	{
	  $errStr = 'Could not load data from remote schema of DiGIR protocol';

	  $loadedRemoteSchema = 0;
	}
    }

  if ($loadedRemoteSchema and $xpm->match($pathToSchemaVersion))
    {
      $rawVersionContent = $xpm->getData($pathToSchemaVersion);

      # See content of $schemaVersionExample above
      if (preg_match($schemaVersionRegexp, $rawVersionContent, $matches))
	{
	  $_SESSION['remoteVersion']['number'] = $matches[1];
	  $_SESSION['remoteVersion']['lastchanged'] = $matches[2];
	  $_SESSION['remoteVersion']['responsible'] = $matches[3];
	}
    }

  if ($loadedRemoteSchema and !isset($_SESSION['remoteVersion']))
    {
      $errStr = 'Could not determine DiGIR protocol version from the remote schema';
    }

  if (count($msgs))
    {
      # Show errors in a list
      die("<ul>\n<li>".implode("\n<li>", $msgs)."\n</ul>");
    }

  $_SESSION['setup'] = 1;
  $welcomeMsg = 'Welcome to the DiGIR provider configurator';
}

# Read resources list configuration file
if (!isset($xpl))
{
  $xpl = new XPath();
  $xpl->setVerbose(0);
  $xpl->setXmlOption(XML_OPTION_CASE_FOLDING,false);
  $xpl->setXmlOption(XML_OPTION_SKIP_WHITE,true);
  $xpl->importFromFile($resourcesFile);
}

# Get browser type to further choose its corresponding CSS --- Temporarily disabled!
#
#if (!isset($_SESSION[client_type]))
#{
#     include("BrowserDetect.class.php");
#
#     $browser = new BrowserDetect();
#
#     $client_type = $browser->check_browser();
#
#     $_SESSION[client_type] = $client_type;
#}

# CSS mapping to be passed as a parameter to xmlForm objects
$cssMapping = array('textinput' => '',
		    'textarea' => '',
		    'select' => '',
		    'label' => 'label',
		    'required' => 'label_required',
		    'div' => array('box1', 'box2'));

###############    Act according to possible parameters   ################
##########################################################################

if (getVal('get_xml'))
{

  //*************************   Return XML file   ***********************//

  switch (getVal('target'))
    {
    case 'providerMeta':

      # Read provider metadata
      $xpp = new XPath();
      $xpp->setVerbose(0);
      $xpp->setXmlOption(XML_OPTION_CASE_FOLDING,false);
      $xpp->setXmlOption(XML_OPTION_SKIP_WHITE,true);
      $xpp->importFromFile($providerFile);
      
      echo $xpp->exportAsXml();
      break;

    case 'resourceList':
      
      echo $xpl->exportAsXml();
      break;

    case 'resource':

      $resourceCode = getVal('code');

      $configFile = getResourceConfigFile($resourceCode, $xpl);

      if ($configFile)
	{
	  $resourceFile = realpath(DIGIR_CONFIG_DIR.'/'.$configFile);

	  $xpr = new XPath();
	  $xpr->setVerbose(0);
	  $xpr->setXmlOption(XML_OPTION_CASE_FOLDING,false);
	  $xpr->setXmlOption(XML_OPTION_SKIP_WHITE,true);
	  $xpr->importFromFile($resourceFile);
	  
	  echo $xpr->exportAsXml();
	}
      else
	{
	  die('<error>Could not find a resource with this code: '.$resourceCode.'</error>');
	}
      break;

    default:

      die('<error>Sorry, cannot give you any XML with incorrect parameters...</error>');
    }

  exit;
}
elseif (getVal('cache_control'))
{

  //********************   Caching of protocol schema  ******************//

  if (getVal('local_refresh'))
    {
      $xps = new XPath();
      $xps->setVerbose(0);
      $xps->setXmlOption(XML_OPTION_CASE_FOLDING,false);
      $xps->setXmlOption(XML_OPTION_SKIP_WHITE,true);
      
      do
	{
	  # Try to load the schema
	  if (!$xps->importFromFile($schemaFile))
	    {
	      $xpsError = $xps->getLastError();
	      $extraStr = ($xpsError) ? ": \n\n$xpsError" : '';
	      
	      $errStr = 'Could not load local copy of DiGIR xml schema '.$extraStr;
	      break;
	    }

	  # Try to get version information
	  if ($xps->match($pathToSchemaVersion))
	    {
	      $rawVersionContent = $xps->getData($pathToSchemaVersion);
	      
	      if (!preg_match($schemaVersionRegexp, $rawVersionContent, $matches))
		{
		  $errStr = 'Could not determine DiGIR protocol version from the local copy of the schema. Does it have an xsd:documentation element with this pattern?'."\n\n".$schemaVersionExample;
		  break;
		}
	    }

	  $_SESSION['localVersion']['number'] = $matches[1];
	  $_SESSION['localVersion']['lastchanged'] = $matches[2];
	  $_SESSION['localVersion']['responsible'] = $matches[3];

	} while(false);
    }
  elseif (getVal('clear'))
    {
      $cache = new Cache('file', $cacheOptions);

      $cache->flush('configurator');

      $remoteSchemaLocation = getVal('remotelocation');
      $cacheId = $cache->generateID('remoteSchemaLocation');
      $cache->save($cacheId, $remoteSchemaLocation, 0, 'configurator');
    }
  # remote refresh or update local copy
  else
    {
      $remoteSchemaLocation = getVal('remotelocation');
      
      $xps = new XPath();
      $xps->setVerbose(0);
      $xps->setXmlOption(XML_OPTION_CASE_FOLDING,false);
      $xps->setXmlOption(XML_OPTION_SKIP_WHITE,true);
      
      do
	{
	  # Try to load the schema
	  if (!$xps->importFromFile($remoteSchemaLocation))
	    {
	      $xpsError = $xps->getLastError();
	      $extraStr = ($xpsError) ? ": \n\n$xpsError" : '';
	      
	      $errStr = 'Could not load DiGIR xml schema from the remote location specified (<a href="'.$remoteSchemaLocation.'" target="_new">'.$remoteSchemaLocation.'</a>)'.$extraStr;
	      break;
	    }

	  # Try to get version information
	  if ($xps->match($pathToSchemaVersion))
	    {
	      $rawVersionContent = $xps->getData($pathToSchemaVersion);
	      
	      if (!preg_match($schemaVersionRegexp, $rawVersionContent, $matches))
		{
		  $errStr = 'Could not determine DiGIR protocol version from the remote schema specified  (<a href="'.$remoteSchemaLocation.'" target="_new">'.$remoteSchemaLocation.'</a>). Does it have an xsd:documentation element with this pattern?'."\n\n".$schemaVersionExample;
		  break;
		}
	    }
	  
	  if (getVal('update'))
	    {
	      # Try to save local copy
	      if (!$xps->exportToFile($schemaFile))
		{
		  $xpsError = $xps->getLastError();
		  $extraStr = ($xpsError) ? ": \n\n$xpsError" : '';
		  
		  $errStr = 'Could not create a local copy of DiGIR xml schema using the specified remote file (<a href="'.$remoteSchemaLocation.'" target="_new">'.$remoteSchemaLocation.'</a>)'.$extraStr;
		  break;
		}
	      else
		{
		  $msg = 'Local copy of DiGIR xml schema was successfully updated!';
		  
		  $_SESSION['localVersion']['number'] = $matches[1];
		  $_SESSION['localVersion']['lastchanged'] = $matches[2];
		  $_SESSION['localVersion']['responsible'] = $matches[3];
		}
	    }
	  
	  # No errors? Then update cache and session data...
	  $cache = new Cache('file', $cacheOptions);
	  $cacheId = $cache->generateID('remoteSchemaLocation');
	  
	  $cache->save($cacheId, $remoteSchemaLocation, 0, 'configurator');
	  
	  $_SESSION['remoteVersion']['location'] = $remoteSchemaLocation;
	  $_SESSION['remoteVersion']['number'] = $matches[1];
	  $_SESSION['remoteVersion']['lastchanged'] = $matches[2];
	  $_SESSION['remoteVersion']['responsible'] = $matches[3];
	  
	} while(false);
    }
}
elseif (getVal('show_provider_form'))
{

  //*********************   Update Provider Metadata  *******************//

  if (!isset($xpp))
    {
      $xpp = new XPath();
      $xpp->setVerbose(0);
      $xpp->setXmlOption(XML_OPTION_CASE_FOLDING,false);
      $xpp->setXmlOption(XML_OPTION_SKIP_WHITE,true);
      $xpp->importFromFile($providerFile);
    }

  if (!isset($xps))
    {
      $xps = new XPath();
      $xps->setVerbose(0);
      $xps->setXmlOption(XML_OPTION_CASE_FOLDING,false);
      $xps->setXmlOption(XML_OPTION_SKIP_WHITE,true);
      $xps->importFromFile($schemaFile);
    }

  $invisibleElements = array('accessPoint', 'implementation', 'resource');
  
  $strInvisible = "@name='" . implode("' or @name='", $invisibleElements) . "'";
  
  $pathToElementsInSchema = "//xsd:element[@name='provider']/xsd:complexType/xsd:sequence/xsd:element[not($strInvisible)]";
  
  $enclosingPathInXML = '/metadata';

  $cache = new Cache('file', $cacheOptions);
  $cacheId = $cache->generateID('provForm_'.$_SESSION['localVersion']['number']);

  $cachedForm = $cache->get($cacheId, 'configurator');

  # Warning: If instead of passing an xpath object reference (xpp) to xmlForm you
  #          pass the string $providerFile, the internal xpath object created inside
  #          xmlForm may conflict with another xpp which is created to get the 
  #          provider name just before beginning to output html (saw this behaviour
  #          using Apache/Win2k - it does not seem to happen with Apache/Linux)

  if ($cachedForm)
    {
      $form = unserialize($cachedForm);
      $form->setSchema($xps);
      $form->setXml($xpp);
    }
  else
    {
      $form = new xmlForm($xps, $xpp, $cssMapping);
      $form->loadElements($pathToElementsInSchema);

      $cache->save($cacheId, serialize($form), $cacheExpires, 'configurator');
    }

  if (getVal('update_provider'))
    {
      if (!$form->save($pathToElementsInSchema, $enclosingPathInXML, $invisibleElements))
	{
	  $errStr = "Unfortunately, it wasn't possible to save your changes yet:\n\n" . 
	            implode('\n', $form->getErrors());
	}
      else
	{
	  $msg = $savedChangesMsg;
	}
    }
  elseif (getVal('warn_about_saving'))
    {
      $msg = $warnAboutSavingMsg;
    }
}
else if (getVal("remove_resource"))
{

  //*************************   Remove Resource   ***********************//

  do
    {
      $resourceCode = getVal('res', '');

      if (empty($resourceCode))
	{
	  $errStr = 'You must specify a resource!';
	  break;
	}

      $resourceFile = realpath(DIGIR_CONFIG_DIR.'/'.getResourceConfigFile($resourceCode, $xpl));

      # First remove the config file
      if (file_exists($resourceFile))
	{
	  if (!unlink($resourceFile))
	    {
	      $errStr = 'Could not remove resource configuration file!';
	      break;
	    }
	}

      if (!$xpl->removeChild(sprintf("/resources[1]/resource[@name='%s']", getVal('res'))))
	{
	  $errStr = sprintf("Could not remove resource from the resources list file: %s", 
			    $xpl->getLastError());
	  break;
	}

      if (!$xpl->exportToFile($resourcesFile))
	{
	  $errStr = sprintf("Could not save resource list file: %s", $xpl->getLastError());
	  break;
	}

      unset($section);
      unset($resourceCode);
      unset($_REQUEST['res']);
      unset($_REQUEST['clicked']);
      unset($_REQUEST['show_resource_form']);

      $xpl->reindexNodeTree();
    } while(false);
}
else if (getVal('show_resource_form'))
{

  //*************************   Create / Update Resource   ***********************//

  # Unset session variables if necessary
  if (getVal('reset'))
    {
      unset($_SESSION['metadataTag']);
      unset($_SESSION['datasourceTag']); 
      unset($_SESSION['tableTag']); 
      unset($_SESSION['filterTag']); 
      unset($_SESSION['mappingTag']); 
   }

  # Default section is "metadata"
  $section = getVal('section', 'metadata');

  $resourceCode = getVal('res', '');

  $resourceFile = getVal('new') ? '': realpath(DIGIR_CONFIG_DIR.'/'.getResourceConfigFile($resourceCode, $xpl));

  if ($section == 'metadata')
    {

      //***************  Metadata  ***************//

      # Quick hack to add conceptual schema default value
      if (getVal('new') and 
	  !getVal(urlencode('conceptualSchema[1]')) and
	  !getVal(urlencode('conceptualSchema[1][@schemaLocation]')))
	{
	  $_REQUEST[urlencode('conceptualSchema[1]')] = 'http://digir.net/schema/conceptual/darwin/2003/1.0';
	  $_REQUEST[urlencode('conceptualSchema[1][@schemaLocation]')] = 'http://digir.net/schema/conceptual/darwin/2003/1.0/darwin2.xsd';
	}

      $invisibleElements = array('code', 'numberOfRecords', 'dateLastUpdated');
      
      $strInvisible = "@name='" . implode("' or @name='", $invisibleElements) . "'";
      
      $pathToElementsInSchema = "//xsd:element[@name='provider']/xsd:complexType/xsd:sequence/xsd:element[@name='resource']/xsd:complexType/xsd:sequence/xsd:element[not($strInvisible)]";
  
      $enclosingPathInXML = '/configuration/metadata';

      $cache = new Cache('file', $cacheOptions);
      $cacheId = $cache->generateID('resForm_'.$_SESSION['localVersion']['number']);
      
      $cachedForm = $cache->get($cacheId, 'configurator');
      
      if ($cachedForm)
	{
	  $form = unserialize($cachedForm);
	  $form->setSchema($schemaFile);
	  $form->setXml($resourceFile);
	}
      else
	{
	  $form = new xmlForm($schemaFile, $resourceFile, $cssMapping);
	  $form->loadElements($pathToElementsInSchema);
	  
	  $cache->save($cacheId, serialize($form), $cacheExpires, 'configurator');
	}

      if (getVal('update_resource'))
	{
	  if ($resourceCode <> getVal('newRes'))
	    {
	      $pathToNewResource = sprintf("//resources/resource[@name='%s']", getVal('newRes'));

	      # If user changed the resource code, check if it is unique
	      $n = $xpl->match($pathToNewResource);
	      
	      if (count($n))
		{
		  $errStr = 'This resource code already exists, please choose another one';
		}
	    }

	  if (empty($errStr))
	    {
	      # Store local copy of the schemas if they are not already available
	      # note: I am not happy to get the xml here, since the $form save method
              #       needs to do it again later... (maybe we can cache the resulting
              #       xml in a $form property and add a parameter $force save method?)
	      $tmpXml = $form->getXml($pathToElementsInSchema);

	      if (empty($tmpXml))
		{
		  $errStr = "Could not generate XML!\n\n" . implode("<br>", $form->getErrors());
		}
	      else
		{
		  $errStr = handleSchemas($tmpXml, DIGIR_CONFIG_DIR);
		}
	    }
	  
	  if (empty($errStr))
	    {
	      if (!$form->save($pathToElementsInSchema, $enclosingPathInXML, $invisibleElements))
		{
		  $errStr = "Unfortunately, it wasn't possible to save your changes yet:\n\n" . 
		    implode("<br>", $form->getErrors());
		}
	      else
		{
		  $pathToLastResource = sprintf("//resources/resource[@name='%s']", $resourceCode);
		  
		  if ($resourceCode <> getVal('newRes'))
		    {
		      # If user changed resource code, save it
		      if ($xpl->setAttributes($pathToLastResource, 
					       array('name'=>getVal('newRes'))) and
			  $xpl->exportToFile($resourcesFile))
			{
			  $resourceCode = getVal('newRes');
			}
		      else
			{
			  $errStr = 'Could not update resource code: '. $xpl->getLastError();
			}
		    }
		  
		  if (empty($errMsg))
		    {
		      $msg = $savedChangesMsg;
		    }
		}
	    }
	}
      elseif (getVal('next'))
	{
	  $xmlMetadata = $form->getXml($pathToElementsInSchema);

	  if ($xmlMetadata)
	    {
	      # Store local copy of the schemas if they are not already available
	      $errStr = handleSchemas($xmlMetadata, DIGIR_CONFIG_DIR);

	      if (empty($errStr))
		{
		  $_SESSION['metadataTag'] = '<metadata>'.$xmlMetadata.'</metadata>';
		  $section = 'datasource';
		  unset($_REQUEST['clicked']);
		  unset($_REQUEST['next']);
		}
	    }
	  else
	    {
	      $errStr = "Please, correct the following errors before proceeding:\n\n" . 
		         implode("\n", $form->getErrors());
	    }
	}
      elseif (getVal('reset') and !getVal('new'))
	{
	  # Entering edition of an existing resource
	  $msg = $warnAboutSavingMsg;
	}
    }

  if ($section == 'datasource')
    {

      //***************  Datasource  ***************//

      if (getVal('new'))
	{
	  $resourceCode = getVal('newRes', getVal('res'));
	}
      else
	{
	  $xpr = new XPath();
	  $xpr->setVerbose(1);
	  $xpr->setXmlOption(XML_OPTION_CASE_FOLDING,false);
	  $xpr->setXmlOption(XML_OPTION_SKIP_WHITE,true);
	  $xpr->importFromFile($resourceFile);
	  
	  $pathToDatasource = '/configuration/datasource';
	}
      
      # Does anyone know a way to get this list dynamically from php???
      $fullListOfEncodings = 'UCS-4, UCS-4BE, UCS-4LE, UCS-2, UCS-2BE, UCS-2LE, UTF-32, UTF-32BE, UTF-32LE, UCS-2LE, UTF-16, UTF-16BE, UTF-16LE, UTF-8, UTF-7, ASCII, EUC-JP, SJIS, eucJP-win, SJIS-win, ISO-2022-JP, JIS, ISO-8859-1, ISO-8859-2, ISO-8859-3, ISO-8859-4, ISO-8859-5, ISO-8859-6, ISO-8859-7, ISO-8859-8, ISO-8859-9, ISO-8859-10, ISO-8859-13, ISO-8859-14, ISO-8859-15, byte2be, byte2le, byte4be, byte4le, BASE64, 7bit, 8bit, UTF7-IMAP, EUC-CN, CP936, HZ, EUC-TW, CP950, BIG-5, EUC-KR, UHC (CP949), ISO-2022-KR, Windows-1251 (CP1251), Windows-1252 (CP1252), CP866, KOI8-R';

      $tmp = split(', ', $fullListOfEncodings);
      sort($tmp);
      
      $encodings = getHash($tmp);
      
      # ADOdb drivers
      $adodbDrivers = array(
			    '' => '-- Select --',
			    'ado' => 'ADO generic driver',
			    'db2' => 'DB2',
			    'firebird' => 'Firebird version of interbase',
			    'fbsql' => 'FrontBase',
			    'informix' => 'Informix generic driver',
			    'informix72' => 'Informix databases before version 7.3',
			    'borland_ibase' => 'Interbase 6.5 or later (Borland version)',
			    'ibase' => 'Interbase 6 or earlier',
			    'access' => 'Microsoft Access/Jet',
			    'ado_access' => 'Microsoft Access/Jet (using ADO)',
			    'ado_mssql' => 'Microsoft SQL Server (using ADO)',
			    'mssqlpo' => 'Microsoft SQL Server (portable driver)',
			    'mssql' => 'Microsoft SQL Server 7 and later',
			    'odbc_mssql' => 'Microsoft SQL Server (using ODBC)',
			    'vfp' => 'Microsoft Visual FoxPro',
			    'mysqlt' => 'MySQL with transaction support',
			    'mysql' => 'MySQL without transaction support',
			    'odbc' => 'ODBC generic driver',
			    'oci8' => 'Oracle 8/9',
			    'oci8po' => 'Oracle 8/9 portable driver',
			    'oci805' => 'Oracle 8.0.5',
			    'oracle' => 'Oracle 7 (old client API)',
			    'odbc_oracle' => 'Oracle (using ODBC)',
			    'postgres' => 'PostgreSQL generic driver',
			    'postgres7' => 'PostgreSQL 7 or later',
			    'postgres64' => 'PostgreSQL 6.4 and earlier',
			    'sqlanywhere' => 'SQL Anywhere (Sybase)',
			    'sybase' => 'Sybase'
			    );

      if (!getVal('new')) array_shift($adodbDrivers);

      # Connection string templates
      $adodbTemplates = array(
			      'ado_access' => 'Provider=Microsoft.JET.OLEDB.4.0;Data Source="c:\MyDirectory\MyDatabase.mdb"'
			      );

      if (getVal('update_resource') or getVal('next'))
	{
	  $type     = getVal('type');
	  $constr   = getVal('constr');
	  $uid      = getVal('uid');
	  $pwd      = getVal('pwd');
	  $database = getVal('database');
	  $dbtype   = getVal('dbtype');
	  $encoding = getVal('encoding');

	  # Try connecting to database
	  do
	    {
	      if ($type == 'SQL' and (empty($dbtype) or (empty($constr) and empty($database))))
		{
		  $errStr = "Please, provide a minimal set of data to connect to a SQL database!\nIt won't be possible to continue without openning a connection.";
		  break;
		}

	      $cn = &ADONewConnection($dbtype);
	      if (!is_object($cn))
		{
		  $errStr = "Could not create connection using database type '$dbtype'!";
		  break;
		}

	      # Xpath does not convert special chars back to their original form, so:
	      $cleanConstr = str_replace(array('&quot;', '&amp;'), 
					 array('"'     , '&'), 
					 $constr);
	      
	      $retVal = $cn->PConnect($cleanConstr, $uid, $pwd, $database);
	      if (!$retVal)
		{
		  $errStr = "Could not open a database connection using these settings!\n";
		  $adoError = $cn->errorMsg();
		  
		  if ($adoError)
		    {
		      $errStr .= "ADODB: $adoError";
		    }
		  
		  $cn->Close();
		  break;
		}

	      # Ok, save changes to config file
	      $attributes = array('type'     => $type,
				  'constr'   => escapeXMLSpecialChars($constr),
				  'uid'      => escapeXMLSpecialChars($uid),
				  'pwd'      => escapeXMLSpecialChars($pwd),
				  'database' => escapeXMLSpecialChars($database),
				  'dbtype'   => $dbtype,
				  'encoding' => $encoding);

	      if (getVal('update_resource'))
	      {
		# Existing resource
		if (!$xpr->setAttributes($pathToDatasource, $attributes) or
		    !$xpr->exportToFile($resourceFile))
		  {
		    $errStr = sprintf("Could not update attribute '%s': %s", 
				      $attrName, $xpr->getLastError());
		    break;
		  }
		
		$msg = $savedChangesMsg;
	      }
	      else
		{
		  # New resource
		  $xmlDatasource = '<datasource';

		  foreach ($attributes as $key=>$val)
		    {
		      $xmlDatasource .= " $key=\"$val\"";
		    }
		  
		  $xmlDatasource .= ' />';

		  $_SESSION['datasourceTag'] = $xmlDatasource;
		  $section = 'tables';
		  unset($_REQUEST['clicked']);
		  unset($_REQUEST['next']);
		}
	      
	      $cn->Close();
	      
	    } while(false);
	}
      elseif (getVal('refresh'))
	{
	  $type     = getVal('type');
	  $constr   = getVal('constr');
	  $uid      = getVal('uid');
	  $pwd      = getVal('pwd');
	  $database = getVal('database');
	  $dbtype   = getVal('dbtype');
	  $encoding = getVal('encoding');

	  if (isset($adodbTemplates[$dbtype]))
	    {
	      $template = urlencode(htmlspecialchars($adodbTemplates[$dbtype]));

	      $templateTitle = urlencode(htmlspecialchars("Connection string template\n".$adodbDrivers[$dbtype]));
	    }
	}
      elseif (getVal('new'))
	{
	  # Default values for new datasources
	  $type     = 'SQL';
	  $constr   = '';
	  $uid      = '';
	  $pwd      = '';
	  $database = '';
	  $dbtype   = '';
	  $encoding = 'ISO-8859-1';
	}
      else
	{
	  # Values inside XML
	  $datasourceSettings = $xpr->getAttributes($pathToDatasource);
	  
	  $type     = $datasourceSettings['type'];
	  $constr   = $datasourceSettings['constr'];
	  $uid      = $datasourceSettings['uid'];
	  $pwd      = $datasourceSettings['pwd'];
	  $database = $datasourceSettings['database'];
	  $dbtype   = $datasourceSettings['dbtype'];
	  $encoding = $datasourceSettings['encoding'];
	}
    }

  if ($section == 'tables')
    {

      //***************  Tables  ***************//

      $labelInFirstJoinCombo  = '-- between --';
      $labelInSecondJoinCombo = '-- and --';

      $xpr = new XPath();
      $xpr->setVerbose(1);
      $xpr->setXmlOption(XML_OPTION_CASE_FOLDING,false);
      $xpr->setXmlOption(XML_OPTION_SKIP_WHITE,true);

      if (getVal('new'))
	{
	  $xpr->importFromString($_SESSION['datasourceTag']);
	  
	  $pathToDatasource = '/datasource';
	}
      else
	{
	  $xpr->importFromFile($resourceFile);
	  
	  $pathToDatasource = '/configuration/datasource';
	}

      # Get datasource settings
      $datasourceSettings = $xpr->getAttributes($pathToDatasource);

      $type     = $datasourceSettings['type'];
      $constr   = str_replace(array('&quot;', '&amp;'), 
			      array('"'     , '&'), 
			      $datasourceSettings['constr']);
      $uid      = $datasourceSettings['uid'];
      $pwd      = $datasourceSettings['pwd'];
      $database = $datasourceSettings['database'];
      $dbtype   = $datasourceSettings['dbtype'];
      
      do
	{
	  # Try connecting to database
	  $cn = &ADONewConnection($dbtype);
	  if (!is_object($cn))
	    {
	      $errStr = "Could not create connection using database type '$dbtype'!";
	      break;
	    }
	  
	  $retVal = $cn->PConnect($constr, $uid, $pwd, $database);
	  if (!$retVal)
	    {
	      $errStr = 'Could not open a database connection using these settings!';
	      $adoError = $cn->errorMsg();
	      
	      if ($adoError)
		{
		  $errStr .= "\nADODB: $adoError";
		}
	      
	      $cn->Close();
	      break;
	    }

	  # Get tables from db
	  $tables = getHash($cn->MetaTables());
	  
	  if (!count($tables))
	    {
	      $errStr = 'No tables inside database!';
	      $cn->Close();
	      break;
	    }
	  
	  $pathToTable = '/configuration[1]/table[1]';

	  # Note: Since user can play with tables and joins without saving, we
	  #       need to store a temporary xml string representing the "table" tag.
	  #       Better not to use post vars because there are also links (get method)
	  #       and the parameter value can be long. So, decision was to use a session 
	  #       variable. New resources use $_SESSION['tableTag'], while existing 
	  #       resources use $_SESSION['tableTag_'.$resourceCode]
	  # Warning: This solution is not perfect! If an user edit a resource, change the
	  #          table configuration without saving, then open the same resource in 
	  #          another browser window, change its code, return to the previous window
	  #          and try to save the table changes - boom! 

	  # Session control
	  $tableTagId =  'tableTag';

	  if (!getVal('new'))
	    {
	      $tableTagId .= '_'.$resourceCode;
	    }

	  # Get xml to work with
	  if (getVal('clicked'))
	    {
	      # Clicked somewhere (add/remove join, save changes or select root table)

	      if (!isset($_SESSION[$tableTagId]))
		{
		  $errStr = 'Fatal error: Problems with session control!';
		  $cn->Close();
		  break;
		}
	      
	      # Get data from session
	      $xmlTable = $_SESSION[$tableTagId];

	      $root = getVal('root', '0');
	      
	      if (!empty($root))
		{
		  $rootTable = substr($root, 0, strpos($root, '.'));
		  $rootKey   = substr($root, strpos($root, '.')+1);
		}
	      else
		{
		  $rootTable = $rootKey = '';
		}
	    }
	  elseif (getVal('new'))
	    {
	      # New resource file
	      # important: this scope should be here, after getVal("clicked"),
	      #            since variable "new" is passed as parameter when
	      #            doing changes.
	      $xmlTable = '<table name="" key="" />';
	      
	      $root = '0';

	      $_SESSION[$tableTagId] = $xmlTable;
	    }
	  else
	    {
	      # Entering edition of existing resource
	      $xmlTable = $xpr->exportAsXml($pathToTable, ''); # second parameter should be empty (no header)
	      
	      $_SESSION[$tableTagId] = $xmlTable;
	      
	      $rootTable = $xpr->getAttributes($pathToTable, 'name');
	      $rootKey   = $xpr->getAttributes($pathToTable, 'key');
	      
	      $root = $rootTable . '.'. $rootKey;
	    }
	  
	  $xpt = new XPath();
	  $xpt->setVerbose(1);
	  $xpt->setXmlOption(XML_OPTION_CASE_FOLDING,false);
	  $xpt->setXmlOption(XML_OPTION_SKIP_WHITE,true);
	  $xpt->importFromString($xmlTable);
	  
	  # This varible indicates that we were able to connect to database and
	  # understand the request (in other words, $xpt and $tables must be defined)
	  $setupXml = true;

	  # Do any change if necessary
	  if (getVal('update_resource'))
	    {
	      # Get the table names from the original and new set of tables
	      $originalTableSet = $newTableSet = array();

	      $pathToTables = '//table';

	      $xmatch = $xpr->match($pathToTables);

	      foreach ($xmatch as $pathToTab)
		{
		  $tabName = $xpr->getAttributes($pathToTab, 'name');
		  array_push($originalTableSet, $tabName);
		}

	      $xmatch = $xpt->match($pathToTables);

	      foreach ($xmatch as $pathToTab)
		{
		  $tabName = $xpt->getAttributes($pathToTab, 'name');
		  array_push($newTableSet, $tabName);
		}

	      # Remove tag from resource file
	      if (!$xpr->removeChild($pathToTable))
		{
		  $errStr = sprintf("Could not prepare updating: %s", $xpr->getLastError());
		  break;
		}
	      
	      # Add new tag from environment
	      if (!$xpr->insertChild('/configuration[1]/datasource[1]', $xmlTable, false))
		{
		  $errStr = sprintf("Could not update content: %s", $xpr->getLastError());
		  break;
		}

	      # Remove concepts that were mapped using tables that have been removed
	      $removedTables = array_diff($originalTableSet, $newTableSet);

	      foreach ($removedTables as $tabName)
		{
		  if (!$xpr->removeChild(sprintf("/configuration[1]/concepts[1]/concept[@table='%s']", $tabName)))
		    {
		      $errStr = sprintf("Could not remove all references to table '%s' from the mapped concepts: %s", $tabName, $xpr->getLastError());
		      break;
		    }
		}
	      
	      # Save file
	      if (!$xpr->exportToFile($resourceFile, '', XML_HEADER))
		{
		  $errStr = sprintf("Could not save resource file: %s", $xpr->getLastError());
		  break;
		}
	      
	      $msg = $savedChangesMsg;
	    }
	  elseif (getVal('next'))
	    {
	      if (empty($root))
		{
		  $errStr = 'Please, select at least a root table and its key field!';
		  break;
		}

	      if (getVal('from') <> '0' and getVal('to') <> '0')
		{
		  $errStr = 'The new join you have just selected will not be automatically added. Please, click on the "add" button or unselect the values before beginning the next step.';
		  break;
		}

	      # New resource (tableTag id already defined in session)
	      $section = 'filter';
	      unset($_REQUEST['clicked']);
	      unset($_REQUEST['next']);
	    }
	  elseif (getVal('refresh', '') == 'root')
	    {
	      if (!$xpt->setAttributes('/table[1]', array('name' => $rootTable, 
							  'key' => $rootKey)))
		{
		  $errStr = sprintf("Could not update root settings: %s", $xpt->getLastError());
		  break;
		}
	      
	      $xmlTable = $xpt->exportAsXml('', ''); # second parameter should be empty (no header)
              
	      $_SESSION[$tableTagId] = $xmlTable;
	    }
	  elseif (getVal('remove'))
	    {
	      $pathToRemove = urldecode(getVal('remove'));

	      # Remove tag from environment
	      if (!$xpt->removeChild($pathToRemove))
		{
		  $errStr = sprintf("Could not remove node: %s", $xpt->getLastError());
		  break;
		}
	      
	      $xmlTable = $xpt->exportAsXml('', ''); # second parameter should be empty (no header)
	      
	      $_SESSION[$tableTagId] = $xmlTable;
	    }
	  elseif (getVal('addjoin') or getVal('refresh', '') == 'addjoin')
	    {
	      if (!getVal('from') or !getVal('to'))
		{
		  $errStr = 'Please select a value from both combos before adding!';
		  break;
		}
	      
	      $fromTablePlusField = getVal('from');
	      $toTablePlusField = getVal('to');
	      
	      $fromTable = substr($fromTablePlusField, 0, strpos($fromTablePlusField, '.'));
	      $fromField = substr($fromTablePlusField, strpos($fromTablePlusField, '.')+1);
	      
	      $toTable = substr($toTablePlusField, 0, strpos($toTablePlusField, '.'));
	      $toField = substr($toTablePlusField, strpos($toTablePlusField, '.')+1);
	      
	      $parentCandidates = $xpt->match(sprintf("//table[@name='%s']", $fromTable));
	      
	      if (!count($parentCandidates))
		{
		  $errStr = "Could not find parent node named '$fromTable'!";
		  break;
		}
	      
	      if (!$xpt->appendChild($parentCandidates[0], 
				     sprintf("<table name=\"%s\" key=\"%s\" join=\"%s\" />", 
					     $toTable, $toField, $fromField)))
		{
		  $errStr = sprintf("Could not add new join: %s", $xpt->getLastError());
		  break;
		}
	      
	      $xmlTable = $xpt->exportAsXml('', ''); # second parameter should be empty (no header)
	      
	      $_SESSION[$tableTagId] = $xmlTable;
	      
	      unset($_REQUEST['from']);
	      unset($_REQUEST['to']);
	    }
	  
	} while(false);

      # If we were able to connect with database and understand the request
      # (In other words: if $xpt and $tables are defined)
      if ($setupXml)
	{
	  # Prepare data to use on combos
	  # Get all tables inside xml
	  $tablesInsideXml = array();
	  
	  $selectedTables = $xpt->match('//table');
	  
	  foreach ($selectedTables as $xpath)
	    {
	      array_push($tablesInsideXml, $xpt->getAttributes($xpath, 'name'));
	    }
	  
	  # Get all tables plus columns from database and all tables and columns from xml
	  $allTablesAndColumns = array();
	  $tablesAndColumnsInsideXml = array();
	  
	  foreach ($tables as $table)
	    {
	      $columns = $cn->MetaColumnNames($table);
	      
	      foreach ($columns as $column)
		{
		  array_push($allTablesAndColumns, $table . '.' . $column);
		  
		  if (in_array($table, $tablesInsideXml))
		    {
		      array_push($tablesAndColumnsInsideXml, $table . '.' . $column);
		    }
		}
	    }

	  if (empty($allTablesAndColumns))
	    {
	      $errStr = 'There was a problem when getting the column names from all tables!';

	      if ($dbtype == 'ado_access')
		{
		  $errStr .= ' It is possible that your access database has one or more broken links to external tables. Please, check it.';
		}
	      else
		{
		  # NOTE: Would it be interesting to provide a way to report problems directly from the configurator??
		  #$errStr .= ' You can report it to the DiGIR developers (and pray :-).';
		}
	    }
	  
	  $tablesAndColumnsOutsideXml = array_diff($allTablesAndColumns, 
						   $tablesAndColumnsInsideXml);
	  
	  $allTablesAndColumns = getHash($allTablesAndColumns);
	  $tablesAndColumnsInsideXml = getHash($tablesAndColumnsInsideXml);
	  $tablesAndColumnsOutsideXml = getHash($tablesAndColumnsOutsideXml);
	  
	  array_unshift($tablesAndColumnsInsideXml , $labelInFirstJoinCombo);
	  array_unshift($tablesAndColumnsOutsideXml, $labelInSecondJoinCombo);

	  # This array will be used as a parameter to "getJoins" indicating which
	  # variables should be passed on links
	  $passVars = array('show_resource_form' => 1,
			    'res' => $resourceCode,
			    'section' => 'tables',
			    'clicked' => 1,
			    'root' => $root);
	  
	  if (getVal('new'))
	    {
	      $passVars['new'] = 1;
	      
	      array_unshift($allTablesAndColumns, '-- select --');
	    }
	}
      
      $cn->Close();
    }


  if ($section == 'filter')
    {

      //***************  Filter  ***************//

      $xpr = new XPath();
      $xpr->setVerbose(1);
      $xpr->setXmlOption(XML_OPTION_CASE_FOLDING,false);
      $xpr->setXmlOption(XML_OPTION_SKIP_WHITE,true);

      if (getVal('new'))
	{
	  $xpr->importFromString('<configuration>'.
				 "\n".$_SESSION['metadataTag'].
				 "\n".$_SESSION['datasourceTag'].
				 "\n".$_SESSION['tableTag'].
				 "\n</configuration>");
	  
	  $pathToDatasource = '//datasource';
	}
      else
	{
	  $xpr->importFromFile($resourceFile);
	  
	  $pathToDatasource = '/configuration/datasource';
	}

      # Get datasource settings
      $datasourceSettings = $xpr->getAttributes($pathToDatasource);

      $type     = $datasourceSettings['type'];
      $constr   = str_replace(array('&quot;', '&amp;'), 
			      array('"'     , '&'), 
			      $datasourceSettings['constr']);
      $uid      = $datasourceSettings['uid'];
      $pwd      = $datasourceSettings['pwd'];
      $database = $datasourceSettings['database'];
      $dbtype   = $datasourceSettings['dbtype'];

      do {
	
	# Try connecting to database
	$cn = &ADONewConnection($dbtype);
	if (!is_object($cn))
	  {
	    $errStr = "Could not create connection using database type '$dbtype'!".
	      " Please check your settings in 'datasource' menu item";
	    break;
	  }
	
	$retVal = $cn->PConnect($constr, $uid, $pwd, $database);
	if (!$retVal)
	  {
	    $errStr = 'Could not open a database connection!'.
	      " Please check your settings in 'datasource' menu item";
	    $adoError = $cn->errorMsg();
	    
	    if ($adoError)
	      {
		$errStr .= "\nADODB: $adoError";
	      }
	    
	    $cn->Close();
	    break;
	  }

	# Get tables involved
	$tables = array();
	
	$selectedTables = $xpr->match('//table');
	
	foreach ($selectedTables as $xpath)
	  {
	    array_push($tables, $xpr->getAttributes($xpath, 'name'));
	  }
	
	$tables = getHash($tables);
	
	# Get column names for each table
	$tablesAndColumnsInsideXml = array();
	  
	foreach ($tables as $table)
	  {
	    $columns = $cn->MetaColumnNames($table);
	    
	    foreach ($columns as $column)
	      {
		array_push($tablesAndColumnsInsideXml, $table . '.' . $column);
	      }
	  }

	$cn->Close();

	$columnsList = getHash($tablesAndColumnsInsideXml);

	array_unshift($columnsList, '-- column --');

	# comparative operators
	$cops = array('equals' => 'equals',
		      'notEquals' => 'does not equal',
		      'lessThan' => 'less than',
		      'lessThanOrEquals' => 'less than or equal to',
		      'greaterThan' => 'greater than',
		      'greaterThanOrEquals' => 'greater than or equal to',
		      'like' => 'contains (% for wildcard)',
		      'in' => 'in list (comma-delimited)');

	$copsList = $cops;

	array_unshift($copsList, '-- comparator --');

	# logical operators
	$lops = array('and' => 'and',
		      'or' => 'or',
		      'andNot' => 'and not',
		      'orNot' => 'or not');

	$lopsList = $lops;

	array_unshift($lopsList, '-- operator --');

	# term data types
	$types = array('text' => 'text:',
		       'numeric' => 'number:',
		       'datetime' => 'date/time:');

	$typesList = $types;

	array_unshift($typesList, '-- type --');

	$pathToFilter = '/configuration[1]/filter[1]';

	# Session control
	$filterTagId = 'filterTag';
	
	if (!getVal('new'))
	  {
	    $filterTagId .= '_'.$resourceCode;
	  }
	
	# Get xml to work with

	if (getVal('clicked'))
	  {
	    # Clicked somewhere (add/remove buttons or save changes)
	    
	    if (!isset($_SESSION[$filterTagId]))
	      {
		$errStr = 'Fatal error: Problems with session control!';
		$cn->Close();
		break;
	      }
	    
	    # Get data from session
	    $xmlFilter = $_SESSION[$filterTagId];
	  }
	elseif (getVal('new'))
	  {
	    # New resource file
	    # important: this scope should be here, after getVal("clicked"),
	    #            since variable "new" is passed as parameter when
	    #            doing changes.
	    $xmlFilter = '<filter />';
	    
	    $_SESSION[$filterTagId] = $xmlFilter;
	  }
	else
	  {
	    # Entering edition of existing resource
	    if ($xpr->match($pathToFilter))
	      {
		$xmlFilter = $xpr->exportAsXml($pathToFilter, ''); # second parameter should be empty (no header)

		# Change the xml filter to something easier to handle:
		# eg. '<equals' becomes '<op type="cop" name="equals"'
		# eg. '</equals>' becomes '</op>'
		# Otherwise, it would be necessary to keep changing tag
		# names as the user make the choices through the interface
                $oldBeginMarks = $oldEndMarks = $newBeginMarks = array();
		
		foreach ($cops as $k => $v)
		  {
		    array_push($oldBeginMarks, "<$k>");
		    array_push($oldEndMarks, "</$k>");
		    array_push($newBeginMarks, '<op type="cop" name="'.$k.'">');
		  }
		foreach ($lops as $k => $v)
		  {
		    array_push($oldBeginMarks, "<$k>");
		    array_push($oldEndMarks, "</$k>");
		    array_push($newBeginMarks, '<op type="lop" name="'.$k.'">');
		  }
		
		$xmlFilter = striReplace($oldBeginMarks, $newBeginMarks, $xmlFilter);
		$xmlFilter = striReplace($oldEndMarks, '</op>', $xmlFilter);
	      }
	    else
	      {
		$xmlFilter = '<filter />';
	      }

	    $_SESSION[$filterTagId] = $xmlFilter;

	    $filterSetup = 1;
	  }

	# Instantiate filter xpath object
	$xpt = new XPath();
	$xpt->setVerbose(0);
	$xpt->setXmlOption(XML_OPTION_CASE_FOLDING,false);
	$xpt->setXmlOption(XML_OPTION_SKIP_WHITE,true);
	$xpt->importFromString($xmlFilter);

	# Additional setup steps that benefit from the existence a PHP.Xpath object
	if ($filterSetup)
	  {
	    # Change list elements (IN operator) to comma separated string
	    $inElements = $xpt->match('//op[@name=in]');

	    foreach ($inElements as $inPath)
	      {
		$inTerms = array();
		
		$els = $xpt->match($inPath.'/list/child::*');
		if (count($els))
		  {
		    # These values should be the same to all children
		    $inTable = $xpt->getAttribute($inPath.'/list/child[1]', 'table');
		    $inField = $xpt->getAttribute($inPath.'/list/child[1]', 'field');
		    $inType  = $xpt->getAttribute($inPath.'/list/child[1]', 'type');
		    
		    foreach ($els as $elPath)
		      {
			array_push($inTerms, trim($xpt->wholeText($elPath)));
		      }
		  }
		
		$xpt->removeChild($inPath.'/list[1]');
		
		$inTerm = sprintf('<term table="%s" field="%s" type="%s">%s</term>', 
				  $inTable, $inField, $inType, implode(',', $inTerms));
		
		$xpt->appendChild($inPath, $inTerm);
	      }

	    # Change xsd:nill attributes to NULL value
	    $nullElements = $xpt->match('//term[@xsi:nill="true"]');

	    foreach ($nullElements as $nPath)
	      {
		$xpt->removeAttribute($nPath, 'xsi:nill');
		$xpt->insertData($nPath, 'NULL');
	      }
	  }

	if (getVal('clicked'))
	  {
	    # Handle possible request to remove element
	    
	    if (getVal('remove'))
	      {
		$elLocation = getVal('refresh');

		if (!$xpt->removeChild($elLocation))
		  {
		    $errStr = sprintf("Could not remove element from filter: %s", 
				      $xpt->getLastError());
		    break;
		  }

		$_SESSION[$filterTagId] = $xpt->exportAsXml('/filter[1]','');
	      }
	    elseif (getVal('add'))
	      {
		$instructions = explode('*', getVal('refresh'));

		$position = substr($instructions[0], 0, strlen($instructions[0])-1);
		$type = substr($instructions[1], -3);

		if ($type == 'lop')
		  {
		    $newTag = '<op type="lop" name="0" />';
		  }
		else
		  {
		    $newTag = '<op type="cop" name="0">'.
		      '<term table="0" field="0" type="0"></term>'.
		      '</op>';
		  }

		if (!$xpt->appendChild($position, $newTag))
		  {
		    $errStr = sprintf("Could not insert new element to filter: %s", 
				      $xpt->getLastError());
		    break;
		  }

		$_SESSION[$filterTagId] = $xpt->exportAsXml('/filter[1]','');
	      }
	    elseif (getVal('update_resource') or getVal('next'))
	      {
		# If there is a filter
		if (strlen($_SESSION[$filterTagId]))
		  {
		    # Check if all "op"s were defined
		    $allOps = $xpt->match('//op');

		    foreach ($allOps as $oPath)
		      {
			$opName = getVal(urlencode($oPath));

			if (!$opName)
			  {
			    $errStr = 'Each operator must have a valid selection - '.
			      'please make sure you did not leave any open choice';
			    
			    break 2;
			  }

			# Use the same loop to update the underlying xml with 
			# operators choices from the form
			$xpt->setAttribute($oPath, 'name', $opName);
		      }

		    # Check if all terms have table/field/type
		    $allTerms = $xpt->match('//term');

		    foreach ($allTerms as $tPath)
		      {
			$column = getVal(urlencode($tPath .'[@col]'));
			$dataType = getVal(urlencode($tPath .'[@type]'));

			if (!$column or !$dataType) 
			  {
			    $errStr = 'Each comparison must have an associated column and '.
			      'data type - please make sure you did not leave any open choice';
			    
			    break 2;
			  }

			# Use the same loop to update the underlying xml with 
			# term values from the HTML form (table / field / type)
			list($table, $field) = explode('.', $column);

			$xpt->setAttribute($tPath, 'table', $table);
			$xpt->setAttribute($tPath, 'field', $field);
			$xpt->setAttribute($tPath, 'type', $dataType);

			$value = getVal(urlencode($tPath .'[@val]'));
			$xpt->replaceData($tPath, $value);
		      }

		    # Check if all lops have two children
		    if (count($xpt->match('//op[@type="lop" and count(*) < 2]')))
		      {
			$errStr = 'Each logical expression must have two associated terms - '.
			  'please make sure you did not leave any open choice';

			break;
		      }

		    # Change "op" elements to the corret DiGIR notation
		    $newXmlFilter = '<filter>'.exportToDigir($xpt).'</filter>';
		  }
		else
		  {
		    # No filter
		    $newXmlFilter = '';
		  }

		if (getVal('update_resource'))
		  {
		    # If config file has a filter 
		    if ($xpr->match($pathToFilter))
		      {
			# Remove filter from config anyway
			if (!$xpr->removeChild($pathToFilter))
			  {
			    $errStr = sprintf("Could not remove filter: %s", 
					      $xpr->getLastError());
			    break;
			  }
		      }

		    # If user defined a filter
		    if ((strlen($_SESSION[$filterTagId])))
		      {
			# Include new filter element
			if (!$xpr->insertChild('/configuration[1]/table[1]', $newXmlFilter, false))
			  {
			    $errStr = sprintf("Could not include filter: %s", 
					      $xpr->getLastError());
			    break;
			  }
		      }

		    # Save file
		    if (!$xpr->exportToFile($resourceFile, '', XML_HEADER))
		      {
			$errStr = sprintf("Could not save resource file: %s", 
					  $xpr->getLastError());
			break;
		      }
		    
		    $msg = $savedChangesMsg;

		    $_SESSION[$filterTagId] = $xpt->exportAsXml('/filter[1]','');
		  }
		else
		  {
		    # New resource (replace filterTag with new one)
		    $_SESSION[$filterTagId] = $newXmlFilter;

		    $section = 'mapping';
		    unset($_REQUEST['clicked']);
		    unset($_REQUEST['next']);
		  }
	      }
	  }
	
      } while (false);
    }


  if ($section == 'mapping')
    {

      //***************  Mapping  ***************//

      $xpr = new XPath();
      $xpr->setVerbose(1);
      $xpr->setXmlOption(XML_OPTION_CASE_FOLDING,false);
      $xpr->setXmlOption(XML_OPTION_SKIP_WHITE,true);

      if (getVal('new'))
	{
	  $xpr->importFromString('<configuration>'.
				 "\n".$_SESSION['metadataTag'].
				 "\n".$_SESSION['datasourceTag'].
				 "\n".$_SESSION['tableTag'].
				 "\n".$_SESSION['filterTag'].
				 "\n</configuration>");
	  
	  $pathToDatasource = '//datasource';
	}
      else
	{
	  $xpr->importFromFile($resourceFile);
	  
	  $pathToDatasource = '/configuration/datasource';
	}

      # Get datasource settings
      $datasourceSettings = $xpr->getAttributes($pathToDatasource);

      $type     = $datasourceSettings['type'];
      $constr   = str_replace(array('&quot;', '&amp;'), 
			      array('"'     , '&'), 
			      $datasourceSettings['constr']);
      $uid      = $datasourceSettings['uid'];
      $pwd      = $datasourceSettings['pwd'];
      $database = $datasourceSettings['database'];
      $dbtype   = $datasourceSettings['dbtype'];
      
      # Get conceptual schemas involved
      $conceptualSchemas = array();
      
      $schemas = $xpr->match('//conceptualSchema');
      
      $pathToConcepts = '//concepts';
     
      do {
	
	# Try connecting to database
	$cn = &ADONewConnection($dbtype);
	if (!is_object($cn))
	  {
	    $errStr = "Could not create connection using database type '$dbtype'!".
	      " Please check your settings in 'datasource' menu item";
	    break;
	  }
	
	$retVal = $cn->PConnect($constr, $uid, $pwd, $database);
	if (!$retVal)
	  {
	    $errStr = 'Could not open a database connection!'.
	      " Please check your settings in 'datasource' menu item";
	    $adoError = $cn->errorMsg();
	    
	    if ($adoError)
	      {
		$errStr .= "\nADODB: $adoError";
	      }
	    
	    $cn->Close();
	    break;
	  }
	
	# Get tables involved
	$tables = array();
	
	$selectedTables = $xpr->match('//table');
	
	foreach ($selectedTables as $xpath)
	  {
	    array_push($tables, $xpr->getAttributes($xpath, 'name'));
	  }
	
	$tables = getHash($tables);

	# Get column names for each possible table
	$fields = array();
	
	foreach ($tables as $k => $tableName)
	  {
	    $fields[strtolower($tableName)] = $cn->MetaColumnNames($tableName);
	  }
	
	array_unshift($tables, '-- select --');
	
	# Check for schemas
	if (!count($schemas))
	  {
	    $errStr = "No conceptual schemas defined yet! Please set them first using the 'metadata' menu item.";
	    break;
	  }

	# Get namespaces defined as attributes from "concepts" tag
	$conceptsAttributes = $xpr->getAttributes($pathToConcepts);
	
	$namespaces = array_flip($conceptsAttributes);

	$cnt = 0;

	# Store local copy of the schemas not already available and prepare variables
	foreach ($schemas as $pathToSchema)
	  {
	    $cnt++;
	    
	    $schemaLocation  = $xpr->getAttributes($pathToSchema, 'schemaLocation');
	    $schemaNamespace = $xpr->getData($pathToSchema);
	    
	    $schemaName = substr($schemaLocation, strrpos($schemaLocation, '/')+1);
	    
	    $schemaFile = realpath(DIGIR_CONFIG_DIR).'/'.$schemaName;
	    
	    if (!file_exists($schemaFile))
	      {
		# Create local copy
		if (!isURL($schemaLocation) or !strpos($schemaLocation, '.xsd'))
		  {
		    $errStr = "Schema location '$schemaLocation' must be an available URL pointing to an xsd file. Please check its value using the 'metadata' menu item.";
		    break;
		  }

		$xp = new XPath();
		$xp->setVerbose(0);
		$xp->setXmlOption(XML_OPTION_CASE_FOLDING,false);
		$xp->setXmlOption(XML_OPTION_SKIP_WHITE,true);
		
		if (!$xp->importFromFile($schemaLocation) or
		    !$xp->exportToFile($schemaFile))
		  {
		    $errStr = "Could not make local copy of '$schemaFile'!";
		    break;
		  }
	      }
	    else
	      {
		$xp = new XPath();
		$xp->setVerbose(1);
		$xp->setXmlOption(XML_OPTION_CASE_FOLDING,false);
		$xp->setXmlOption(XML_OPTION_SKIP_WHITE,true);
		$xp->importFromFile($schemaFile);
	      }
	    
	    # Get namespace alias
	    $defaultAlias = ($cnt == 1 and getVal('new') and strpos($schemaLocation, 'darwin')) ? 'darwin': $cnt;
	    
	    if (isset($namespaces[$schemaNamespace]))
	      {
		$defaultAlias = $namespaces[$schemaNamespace];
		$defaultAlias = substr($defaultAlias, strpos($defaultAlias, ':')+1);
	      }
	    
	    $namespaceAliasInputName = strtr($schemaNamespace, '.', '_') . '_alias';
	    
	    $namespaceAlias = getVal($namespaceAliasInputName, $defaultAlias);
	    
	    if (getVal('clicked') and !$namespaceAlias)
	      {
		$errStr = "Each namespace needs an alias. Empty aliases were filled with their default values.";
		
		$namespaceAlias = $defaultAlias;
	      }
	    
	    # Add values to hash
	    $conceptualSchemas[$schemaNamespace] = array('xp'       => $xp, 
							 'alias'    => $namespaceAlias,
							 'required' => array());

	    $requiredElements = $xp->match("//xsd:element[@name='requiredList']/xsd:complexType/xsd:sequence/xsd:element");

	    foreach ($requiredElements as $pathToElement)
	      {
		array_push($conceptualSchemas[$schemaNamespace]['required'], 
			   $xp->getAttributes($pathToElement, 'ref'));
	      }
	    
	    # release memory!
	    unset($xp);
	  }
	
	if (getVal('update_resource') or getVal('next'))
	  {
	    # TODO:
	    #        - check for uniqueness of namespace aliases.
	    #        - check if there are concepts related to any namespace 
	    #          not listed in 'conceptualSchema' tags.
	    
	    $namespaces = $concepts = '';
	    
	    $conceptMask = '<concept searchable="%s" returnable="%s" name="%s" type="%s" table="%s" field="%s" zid="%s" />';

	    $missingElements = array();

	    # Automatic generation of ZIDs - use a simple sequence
	    $zid = 0;

	    foreach ($conceptualSchemas as $schemaName => $sh)
	      {
		$elements = $sh['xp']->match("/xsd:schema/xsd:element[@substitutionGroup='digir:searchableData' or @substitutionGroup='digir:returnableData' or @substitutionGroup='digir:searchableReturnableData']");
		
		$namespaces .= ' xmlns:'.$sh['alias'].'="'.$schemaName.'"';
		
		$namespaceAlias = $sh['alias'];
		
		$lastNamespaceAlias = getVal(strtr($schemaName, '.', '_') . '_lastAlias', 
					     $namespaceAlias);
		
		foreach ($elements as $pathToElement)
		  {
		    $zid++;

		    $name = $sh['xp']->getAttributes($pathToElement, 'name');

		    # Last widget names
		    $lastSearchableInputName = "$lastNamespaceAlias:$name".'_searchable';
		    $lastReturnableInputName = "$lastNamespaceAlias:$name".'_returnable';
		    $lastTypeInputName = "$lastNamespaceAlias:$name".'_type';
		    $lastTableInputName = "$lastNamespaceAlias:$name".'_table';
		    $lastFieldInputName = "$lastNamespaceAlias:$name".'_field';
		    $lastAddFieldComboName = "$lastNamespaceAlias:$name".'_newfield';

		    # If element is required, check its mapping
		    if (in_array($name, $sh['required']) and
			(!getVal($lastTableInputName, false) or 
			 (!getVal($lastFieldInputName, false) and !getVal($lastAddFieldComboName, false))))
		    {
		      array_push($missingElements, "$namespaceAlias:$name");
		    }		    

		    # We also need some of the next widget names (may be different
		    # if user changed namespace alias) in case we need to reset any field
		    $searchableInputName = "$namespaceAlias:$name".'_searchable';
		    $returnableInputName = "$namespaceAlias:$name".'_returnable';
		    $tableInputName = "$namespaceAlias:$name".'_table';
		    $fieldInputName = "$namespaceAlias:$name".'_field';

		    $table = getVal($lastTableInputName, '');
		    
		    $field = getVal($lastFieldInputName);
		    
		    $newField = getVal($lastAddFieldComboName);
		    
		    if ($newField)
		      {
			$addedFields = split(',', $field);

			if (!flexInArray(strtolower($newField), $addedFields))
			  {
			    $sep = ($field) ? ',': '';
			    
			    $field .= $sep . $newField;
			  
			    $_REQUEST[$lastFieldInputName] = $field;
			  }
		      }
		    
		    if (empty($field) or empty($table))
		      {
			$searchable = $returnable = '0';
			$table = $field = '';
			unset($_REQUEST[$searchableInputName]);
			unset($_REQUEST[$returnableInputName]);
			$_REQUEST[$tableInputName] = '';
			$_REQUEST[$fieldInputName] = '';
		      }
		    else
		      {
			$searchable = getVal($lastSearchableInputName, '0');
			$returnable = getVal($lastReturnableInputName, '0');

			$concepts .= sprintf($conceptMask, 
					     $searchable, 
					     $returnable, 
					     $namespaceAlias.':'.$name, 
					     getVal($lastTypeInputName, ''), 
					     $table, 
					     $field,
					     $zid);
		      }
		  }
	      }

	    # Force minimal mapping
	    if (getVal('clicked') and count($missingElements))
	      {
		$errStr = 'You still need to map at least: '.implode(', ',$missingElements);
		break;
	      }

	    # Erros caught before building the concepts table are shown now
	    if (getVal('clicked') and strlen($errStr) > 0)
	      {
		break;
	      }
	    
	    $xmlConcepts = "<concepts $namespaces>$concepts</concepts>";

	    if (getVal('update_resource'))
	      {
		# Remove tag from resource file
		if (!$xpr->removeChild($pathToConcepts))
		  {
		    $errStr = sprintf("Could not prepare updating: %s", $xpr->getLastError());
		    break;
		  }

		$afterTag = 'table';

		if ($xpr->match('/configuration[1]/filter[1]'))
		  {
		    $afterTag = 'filter';
		  }
		
		# Add new tag from environment
		if (!$xpr->insertChild('/configuration[1]/'.$afterTag.'[1]', $xmlConcepts, false))
		  {
		    $errStr = sprintf("Could not update content: %s", $xpr->getLastError());
		    break;
		  }
	    
		# Save file
		if (!$xpr->exportToFile($resourceFile, '', XML_HEADER))
		  {
		    $errStr = sprintf("Could not save resource file: %s", $xpr->getLastError());
		    break;
		  }
		
		$msg = $savedChangesMsg;
	      }
	    else
	      {
		# New resource!
		$finalXml = '<configuration>'.
		  "\n".$_SESSION['datasourceTag'].
		  "\n".$_SESSION['tableTag'].
		  "\n".$_SESSION['filterTag'].
		  "\n".$xmlConcepts.
		  "\n".$_SESSION['metadataTag'].
		  "\n</configuration>";

		$xpr = new XPath();
		$xpr->setVerbose(1);
		$xpr->setXmlOption(XML_OPTION_CASE_FOLDING,false);
		$xpr->setXmlOption(XML_OPTION_SKIP_WHITE,true);
		$xpr->importFromString($finalXml);

		# Add "invisible" tags
		if (!$xpr->insertChild('/configuration[1]/metadata[1]/name[1]', 
				       '<code>! set by application (value in resources.xml) !</code>',
				       false) or
		    !$xpr->insertChild('/configuration[1]/metadata[1]/recordBasis[1]', 
				       '<numberOfRecords>! set by application !</numberOfRecords>',
				       false) or
		    !$xpr->insertChild('/configuration[1]/metadata[1]/numberOfRecords[1]', 
				       '<dateLastUpdated>! set by application !</dateLastUpdated>',
				       false))
		  {
		    $errStr = sprintf("Could not add 'invisible' tags to configuration file: %s", 
				      $xpr->getLastError());
		    break;
		  }

		$fName = $resourceCode.'.xml';
		$resourceFile = realpath(DIGIR_CONFIG_DIR).'/'.$fName;
		$fCnt = 0;

		# avoid possible files with same name by appending a simple counter
		while(file_exists($resourceFile))
		  {
		    ++$fCnt;
		    $fName = $resourceCode.$fCnt.'.xml';
		    $resourceFile = realpath(DIGIR_CONFIG_DIR).'/'.$fName;
		  }

		# Save new file
		$retVal = $xpr->exportToFile($resourceFile, '', XML_HEADER);

		if ($retVal)
		  {
		    $resourceLine = '<resource name="'.$resourceCode.'" configFile="'.$fName.'"/>';
		    $defaultPlace = '/resources[1]/resource[last()]';

		    # And add reference in resources.xml file
		    if (!$xpl->insertChild($defaultPlace, $resourceLine, FALSE))
		      {
			$defaultPlace = '/resources[1]';

			if (!$xpl->appendChild($defaultPlace, $resourceLine))
			  {
			    $errStr = sprintf("Could not add reference of new resource to resources list: %s", $xpl->getLastError());
			    break;
			  }
		      }
		    
		    if (!$xpl->exportToFile($resourcesFile))
		      {
			$errStr = sprintf("Could not save list of resources: %s", 
					  $xpl->getLastError());
			break;
		      }

		    unset($section);
		    unset($_REQUEST['new']);
		    unset($_REQUEST['clicked']);

		    $msg = $savedChangesMsg;
		  }
		else
		  {
		    $errStr = sprintf("Could not save new resource file: %s", 
				      $xpr->getLastError());
		    break;
		  }
	      }
	  }
	
      } while (false);
    }
}
else
{
  # nothing to do for now
}

# Read provider config (if not dealing with a recently created provider file)
if (!isset($xpp))
{
  $xpp = new XPath();
  $xpp->setVerbose(0);
  $xpp->setXmlOption(XML_OPTION_CASE_FOLDING,false);
  $xpp->setXmlOption(XML_OPTION_SKIP_WHITE,true);
  $xpp->importFromFile($providerFile);
}
else
{
  # Need to reindex in case user changed provider name (why? I thought it was 
  # automatically reindexed after $form->save()...)
  $xpp->reindexNodeTree();
}

# Get provider name
$provName = $xpp->getData('//metadata/name[1]');

# Get all resources
$resources = getResourceList($xpl);

#######################################################
##                                                   ##
##                       HTML                        ##
##                                                   ##
#######################################################

?><!doctype html public "-//W3C//DTD HTML 4.0 //EN">
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
  <title>DiGIR Configuration Setup</title>
  <link rel="stylesheet" href="<?php print(DIGIRADMIN_CSS_URL); ?>digir.css">
  <!-- link rel="styleSheet" HREF="css/<?php echo ($client_type); ?>.css" TYPE="text/css" TITLE="<?php echo ($client_type); ?> StyleSheet" MEDIA="screen, print" -->

  <script LANGUAGE="JavaScript">
   <!--
   function saveScroll()
   {
     if (!window.scrollTo) return;
     var x = (document.body) ? document.body.scrollLeft : window.pageXOffset;
     var y = (document.body) ? document.body.scrollTop : window.pageYOffset;
     document.forms[1].scroll.value = x + "_" + y;
   }

   function loadScroll()
   {
     if (!window.scrollTo) return;
     var xy = "<?php print(getVal('scroll', '')); ?>";
     if (!xy) return;
     var ar = xy.split("_");
     if (ar.length == 2) scrollTo(parseInt(ar[0]), parseInt(ar[1]));
   }
   -->
  </script>
</head>

<body onLoad="loadScroll();" >
<table width="98%" border="0" align="center" cellspacing="2" cellpadding="4">
  <tr>
    <td width="15%" align="left" valign="top">
      <a href="http://digir.sf.net/"><img src="digir_button.gif" alt="DiGIR" border="0"></a>
    </td>
    <td width="85%" align="left" valign="top">
<center><a href="<?php print(PHP_SELF); ?>"><h3 class="title">Configuration Setup</h3></a></center>
    </td>
  </tr>
  <tr>
    <td class="border" width="15%" align="center" valign="top">

      <!------------------ begin SIDE MENU ------------------>
      <b><span class="tip">provider</span></b><br>
      <a href='<?php print(PHP_SELF); ?>?show_provider_form=1&warn_about_saving=1' class='menu'><?php print($provName); ?></a>
      <hr border="1">
      <b><span class="tip" align="center">resources</span></b><br><br>
      <?php if (count($resources) > 0): ?>
      <table border="0" width="90%" cellspacing="0" cellpadding="2">
        <?php foreach ($resources as $resName => $resConfigFile): ?>
          <tr><td class="menu"><a href='<?php print(PHP_SELF); ?>?res=<?php print($resName) ?>&show_resource_form=1' class='menu'><?php print($resName) ?></a></td></tr>
        <?php endforeach; ?>
      </table>
      <?php else: ?>
        <span class="menu">none</span><br>
      <?php endif; ?>
      <br>
      <?php if(!getVal("show_form_to_add_resource")): ?>
      <form action="<?php print(PHP_SELF); ?>" method="post">
        <input type="hidden" name="new" value="1">
        <input type="hidden" name="reset" value="1">
        <input type="submit" name="show_resource_form" value="add resource">
      </form>
      <?php endif; ?><!------------------ end SIDE MENU ------------------>

    </td>
    <td class="border" width="85%" align="left" valign="top">

     <?php ##################     Provider View     #################### ?> 

      <?php if (getVal("show_provider_form")): ?>

        <center><span class="section">Provider<span></center>

        <?php if ($errStr) printf("\n<br><center><span class=\"error\">%s</span></center>", nl2br($errStr)); ?>
        <?php if ($msg) printf("\n<br><center><span class=\"msg\">%s</span></center>", nl2br($msg)); ?>

        <?php if (!getVal('clicked')): ?><br><center><span class="msg">Required fields have </span><span class="label_required">this color</span></center><?php endif; ?>

        <form action="<?php print(PHP_SELF); ?>" method="post">
        <input type="hidden" name="show_provider_form" value="1">
        <input type="hidden" name="scroll">

        <?php print($form->getHtml($pathToElementsInSchema, $enclosingPathInXML)); ?>

        <br>
        <center><input type="submit" name="update_provider" value="save changes"></center>
        </form>

      <?php ##################     Resource View     #################### ?> 

      <?php elseif (getVal("show_resource_form")): ?>

	<?php # aesthetic stuff...

	$sections = array('metadata'   => array('ord'=>'First', 
						'msg'=>"Configuring new resources involves five steps.\nFirst you should fill this form with metadata about the resource.\nYou can find more information about each field by clicking over them.\n\nImportant notice: the new configuration file will only be saved after finishing the last step!"), 

			  'datasource' => array('ord'=>'Second',
						'msg'=>"Next steps depend on an open connection with your datasource.\nPlease, provide the necessary information here:"), 

			  'tables'     => array('ord'=>'Third',
						'msg'=>"At this point, you'll need to indicate which one is the main table with specimen data (root table), and then link it to more tables that may contain other pieces of information necessary to map with the federation schema elements.\nIf you intend to use DarwinCore but you're not familiar with it, please read its documentation first: <a href=\"http://velo.dyndns.info/digir/ow.asp?DarwinCore\" class=\"msg\" target=\"_new\">http://velo.dyndns.info/digir/ow.asp?DarwinCore</a>."), 

			  'filter'     => array('ord'=>'Fourth',
						'msg'=>"You can optionally set a local filter to select which records you want to provide."), 

			  'mapping'    => array('ord'=>'Last', 
						'msg'=>"Finally, you'll need to map each concept from the federation schema to a field in your datasource:"));

          if (getVal('new'))
            {
	      $mainTitle = 'New resource';
	      $tableWidth = '60%';
	      
	      $wizardLabel = $sections[$section]['ord'].' step:&nbsp;&nbsp;&nbsp;'.ucfirst($section);

	      if (!$msg and !getVal('clicked'))
	      {
		$msg = $sections[$section]['msg'];
	      }
            }
          else
            {
	      $mainTitle = $resourceCode;
	      $tableWidth = '80%';
            }
        ?>

        <!------------------ begin Resource TOP MENU ------------------>
        <center><span class="section"><?php print($mainTitle); ?><span></center>
        <br>
        <table align="center" width="<?php print($tableWidth); ?>" cellspacing="1" cellpadding="1" bgcolor="#999999">
         <tr>
          <?php if(getVal('new')): ?>
            <td align="center" valign="middle" width="100%" bgcolor="#f5f5ff" class="label"><?php print($wizardLabel); ?></td>
          <?php else: ?>
	    <?php foreach ($sections as $sectionName => $sectionProperties): ?>
              <?php 

                $bgColor = '#f5f5ff';
                $class = 'text';

	        if ($sectionName == $section)
                {
		  $bgColor = '#ffffee';
		  $class = 'label';
		}
              ?>
              <td align="center" valign="middle" width="20%" bgcolor="<?php print($bgColor); ?>"><a href="<?php print(PHP_SELF); ?>?show_resource_form=1&section=<?php print($sectionName); ?>&res=<?php print($resourceCode); ?>" class="<?php print($class); ?>"><?php print(ucfirst($sectionName)); ?></a></td>
            <?php endforeach; ?>
          <?php endif; ?>
         </tr>
        </table>
        <!------------------ end Resource TOP MENU ------------------>

        <?php if ($errStr): ?>
          <?php  printf("\n<br><center><span class=\"error\">%s</span></center>", nl2br($errStr)); ?>
        <?php else: ?>
          <?php if ($msg) printf("\n<br><center><span class=\"msg\">%s</span></center>", nl2br($msg)); ?>
        <?php endif; ?>

        <form action="<?php print(PHP_SELF); ?>" method="post">
        <input type="hidden" name="res" value="<?php print($resourceCode) ?>">
        <input type="hidden" name="show_resource_form" value="1">
        <input type="hidden" name="section" value="<?php print($section) ?>">
        <?php if(getVal('new')): ?>
          <input type="hidden" name="new" value="1">
        <?php endif; ?>
        <input type="hidden" name="clicked" value="1">
        <input type="hidden" name="refresh" value="">
        <input type="hidden" name="scroll">

        <?php //***************** Metadata ******************// ?>

        <?php if ($section == 'metadata'): ?>

	<?php if (!getVal('clicked')): ?><center><span class="msg">Required fields have </span><span class="label_required">this color</span></center><?php endif; ?>
         <table width="90%" align="center" border="0">
          <tr>
           <td>
            <?php $codeHelp = 'Unique code to identify the resource (usually the collection code)'; ?>
 	    <a href="help.php?name=Code&doc=<?php print($codeHelp); ?>" class="label_required" onClick="window.open('help.php?name=Code&doc=<?php print($codeHelp); ?>','help','width=400,height=200,menubar=no,toolbar=no,scrollbars=yes,resizable=yes,personalbar=no,locationbar=no,statusbar=no').focus(); return false;" onMouseOver="window.status='<?php print($codeHelp); ?>'; return true;" onMouseOut="window.status=''; return true;" style="text-decoration:none;">Code: </a><br><input type="text" name="newRes" value="<?php print(getVal('newRes', $resourceCode)); ?>" size="10"><br>
            
            <?php print($form->getHtml($pathToElementsInSchema, $enclosingPathInXML)); ?>
           </td>
          </tr>
         </table>
         <br>
         <br>

        <?php //***************** Datasource *****************// ?>

        <?php elseif ($section == 'datasource'): ?>

          <table align="center" width="92%" cellspacing="1" cellpadding="1" bgcolor="#999999">
           <tr bgcolor="#ffffee"><td align="left" valign="middle" width="20%"><span class="label">Type: </span></td><td align="left" valign="middle" width="80%"><?php print(getCombo("type", array("SQL"=>"SQL"), $type)); ?></td></tr>
           <tr bgcolor="#ffffee"><td align="left" valign="middle" width="20%"><span class="label">Driver: </span></td><td align="left" valign="middle" width="80%"><?php print(getCombo("dbtype",$adodbDrivers, $dbtype, false, false, "document.forms[1].refresh.value='showTemplate';document.forms[1].submit()")); ?><br><span class="tip">see: </span><a href="http://phplens.com/lens/adodb/docs-adodb.htm#drivers" class="tip" target="_new">http://phplens.com/lens/adodb/docs-adodb.htm#drivers</a></td></tr>
           <tr bgcolor="#ffffee"><td align="left" valign="middle" width="20%"><span class="label">Database encoding: </span></td><td align="left" valign="middle" width="80%"><?php print(getCombo("encoding", $encodings, $encoding)); ?><br><span class="tip">see: </span><a href="http://www.php.net/manual/en/ref.mbstring.php" class="tip" target="_new">http://www.php.net/manual/en/ref.mbstring.php</a></td></tr>
           <tr bgcolor="#ffffee"><td align="left" valign="middle" width="20%"><span class="label">Datasource string: </span></td><td align="left" valign="middle" width="80%"><?php print(getTextInput("constr", $constr, "80")); ?><?php if ($template): ?><br><span class="tip">there is a template available <a href="help.php?name=<?php print($templateTitle); ?>&doc=<?php print($template); ?>" <?php printf("onClick=\"window.open('help.php?name=%s&doc=%s','help','width=600,height=140,menubar=no,toolbar=no,scrollbars=yes,resizable=yes,personalbar=no,locationbar=no,statusbar=no').focus(); return false;\" style=\"text-decoration:none;\"", $templateTitle, $template); ?>">here</a></span><?php endif; ?></td></tr>
           <tr bgcolor="#ffffee"><td align="left" valign="middle" width="20%"><span class="label">Username: </span></td><td align="left" valign="middle" width="80%"><?php print(getTextInput("uid", $uid, "20")); ?></td></tr>
           <tr bgcolor="#ffffee"><td align="left" valign="middle" width="20%"><span class="label">Password: </span></td><td align="left" valign="middle" width="80%"><?php print(getTextInput("pwd", $pwd, "20", true)); ?></td></tr>
           <tr bgcolor="#ffffee"><td align="left" valign="middle" width="20%"><span class="label">Database name: </span></td><td align="left" valign="middle" width="80%"><?php print(getTextInput("database", $database, "20")); ?></td></tr>
          </table>
          <br>

        <?php //***************** Tables *****************// ?>

        <?php elseif ($section == 'tables'): ?>

           <center>
           <div class="box2" align="left">
           <span class="label">Root table and key field: </span><?php print(getCombo("root", $allTablesAndColumns, $root, false, false, "document.forms[1].refresh.value='root';document.forms[1].submit()")); ?><br>
           <?php if($root): ?>
             <?php print(getJoins(PHP_SELF, $passVars, $xpt, $rootTable)); ?>
             <br><br>
             <span class="label">New join: </span><?php print(getCombo("from", $tablesAndColumnsInsideXml, getVal('from', '0'))); ?>&nbsp;<?php print(getCombo("to", $tablesAndColumnsOutsideXml, getVal('to', '0'))); ?>&nbsp;<input type="submit" name="addjoin" value="add">
           <?php endif; ?>
           </div>
           <br>
           </center>

        <?php //***************** Filter *****************// ?>

        <?php elseif ($section == 'filter'): ?>

           <center>
           <?php print(getFilterElements(PHP_SELF, $xpt)); ?>
           <br><br>
           </center>

        <?php //***************** Mapping *****************// ?>

        <?php elseif ($section == 'mapping'): ?>

          <center>
          <?php if (!getVal('clicked')): ?><span class="text">Required fields have </span><span class="text_required">this color</span><br><?php endif; ?>
	  <?php foreach ($conceptualSchemas as $schemaName => $sh): ?>

	    <?php $aliasInputName = strtr($schemaName, '.', '_') . '_alias'; ?>
	    <?php $lastAliasInputName = strtr($schemaName, '.', '_') . '_lastAlias'; ?>
	    <?php $smartMappingName = strtr($schemaName, '.', '_') . '_automap'; ?>
	    <?php $enableSmartMapping = getVal('new', getVal($smartMappingName, 0)); ?>

 	    <br><span class="label">Namespace: </span><span class="tip"><?php print($schemaName); ?></span><span class="label">&nbsp;&nbsp;&nbsp;alias: </span><input type="text" name="<?php print($aliasInputName); ?>" value="<?php print($sh['alias']); ?>" size="15"><br><?php if (!getVal('new')): ?><br><input type="checkbox" class="checkbox" name="<?php print($smartMappingName); ?>" value="1"<?php if ($enableSmartMapping): ?> checked<?php endif; ?>>&nbsp;<span class="label">"automap" after table selection</span><?php endif; ?><br>
            <input type="hidden" name="<?php print($lastAliasInputName); ?>" value="<?php print($sh['alias']); ?>">

	    <?php $elements = $sh['xp']->match("/xsd:schema/xsd:element[@substitutionGroup='digir:searchableData' or @substitutionGroup='digir:returnableData' or @substitutionGroup='digir:searchableReturnableData']"); ?>

            <table align="center" width="95%" cellspacing="1" cellpadding="1" bgcolor="#999999">
              <tr bgcolor="#ffffee" nowrap="1">
                <td class="label" width="15%">concept</td>
                <td class="label" width="5%" align="center">searchable</td>
                <td class="label" width="5%" align="center">returnable</td>
                <td class="label" width="25%" align="center">table</td>
                <td class="label" width="40%" align="center">field</td>
                <td class="label" width="10%" align="center">type</td>
              </tr>
            <?php foreach ($elements as $pathToElement): ?>

	      <?php print(getConcept($sh, $lastAliasInputName, $xpr, $cn, $tables, $fields, $pathToElement, $enableSmartMapping)); ?>
            <?php endforeach; ?>
            </table>
          <?php endforeach; ?>
          <br>
          </center>

        <?php endif; ?>

         <center>
          <?php if (getVal('new')): ?>
           <?php if ($sections[$section]['ord'] <> 'First'): ?>
             <!-- sorry, no back for now
               <input type="submit" name="back" value="<< back">&nbsp;&nbsp;&nbsp;&nbsp;
             -->
           <?php endif; ?>
           <?php if ($sections[$section]['ord'] <> 'Last'): ?>
             <input type="submit" name="next" value="next step >>">
           <?php else: ?>
             <input type="submit" name="next" value="save new resource">
           <?php endif; ?>
	 <?php elseif (!getVal('next')): ?>
           <script LANGUAGE="JavaScript">
             <!--
             function confirmRemoval()
             {
               var agree = confirm("Are you sure you want to remove this resource?");
               if (agree)
                 return true ;
               else
                 return false ;
             }
             -->
           </script>

           <input type="submit" name="remove_resource" value="remove this resource" onClick="return confirmRemoval()">&nbsp;&nbsp;
           <input type="submit" name="update_resource" value="save changes">
	 <?php endif; ?>
         </center>
        </form>

      <?php ##################     Front page     #################### ?> 

      <?php else: ?>
          <center>
          <span class="section"><?php print($welcomeMsg); ?></span><br>
          <div class="box1">
          <span class="label">Attention: </span><span class="menu">This is a beta version! Remember to backup your config files before playing around!</span>
          </div>
          <?php if ($errStr): ?>
            <?php  printf("\n<br><center><span class=\"error\">%s</span></center>", nl2br($errStr)); ?>
          <?php else: ?>
            <?php if ($msg) printf("\n<br><center><span class=\"msg\">%s</span></center>", nl2br($msg)); ?>
          <?php endif; ?>
          <form action="<?php print(PHP_SELF); ?>" method="post">
          <input type="hidden" name="cache_control" value="1">
          <table width="90%" border="0" cellspacing="0" cellpadding="1">
            <tr>
              <td align="left">
                <span class="label"><u>Local copy of DiGIR protocol schema</u></span>
              </td>
            </tr>
            <tr>
              <td align="left">
                <span class="tip">( location: <?php print(DIGIR_CONFIG_DIR); ?> )</span>
              </td>
            </tr>
            <tr>
              <td align="left">
                <span class="label">CVS version:&nbsp;&nbsp;</span><span class="text"><?php print($_SESSION['localVersion']['number']); ?></span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="label">Last changed:&nbsp;&nbsp;</span><span class="text"><?php print($_SESSION['localVersion']['lastchanged']); ?></span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="label">By:&nbsp;&nbsp;</span><a href="http://sourceforge.net/users/<?php print($_SESSION['localVersion']['responsible']); ?>/" class="text" target="_new"><?php print($_SESSION['localVersion']['responsible']); ?></a>&nbsp;&nbsp;<input type="submit" name="local_refresh" value="Refresh">&nbsp;&nbsp;<input type="submit" name="clear" value="Clear cache"><br><br>
              </td>
            </tr>
            <tr>
              <td align="left">
                <span class="label"><u>Remote repository of DiGIR protocol schema</u></span><br><br>
              </td>
            </tr>
            <tr>
              <td align="left">
                <span class="label">Location:&nbsp;&nbsp;</span><input type="text" name="remotelocation" value="<?php print($_SESSION['remoteVersion']['location']); ?>" size="50" maxlenght="50">&nbsp;&nbsp;<input type="submit" name="remote_refresh" value="Refresh">&nbsp;&nbsp;<input type="submit" name="update" value="Update local copy">
              </td>
            </tr>
            <tr>
              <td align="left">
                <span class="label">CVS version:&nbsp;&nbsp;</span><span class="text"><?php print($_SESSION['remoteVersion']['number']); ?></span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="label">Last changed:&nbsp;&nbsp;</span><span class="text"><?php print($_SESSION['remoteVersion']['lastchanged']); ?></span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="label">By:&nbsp;&nbsp;</span><a href="http://sourceforge.net/users/<?php print($_SESSION['remoteVersion']['responsible']); ?>/" class="text" target="_new"><?php print($_SESSION['remoteVersion']['responsible']); ?></a><br><br>
              </td>
            </tr>
            <tr>
              <td align="left">
                <span class="label"><u>Current configuration files</u></span>
              </td>
            </tr>
            <tr>
              <td align="left">
                <span class="tip">( location: <?php print(DIGIR_CONFIG_DIR); ?> )</span>
              </td>
            </tr>
            <tr>
              <td align="left">
                <span class="label">Provider metadata:&nbsp;&nbsp;</span><a href="<?php print(PHP_SELF); ?>?get_xml=1&target=providerMeta" class="text" target="_new"><?php print(DIGIR_METADATA_FILE); ?></a>&nbsp;&nbsp;&nbsp;&nbsp;<span class="label">Resource list:&nbsp;&nbsp;</span><a href="<?php print(PHP_SELF); ?>?get_xml=1&target=resourceList" class="text" target="_new"><?php print(DIGIR_RESOURCE_FILE); ?></a>
              </td>
            </tr>
            <tr>
              <td align="left"><span class="label">Resources:&nbsp;&nbsp;</span>
              <?php $sep = ''; ?>
              <?php foreach ($resources as $resCode => $resConf): ?><?php print($sep); ?><?php $sep = '<span class="tip">,&nbsp;&nbsp;&nbsp;</span>'; ?><a href="<?php print(PHP_SELF); ?>?get_xml=1&target=resource&code=<?php print($resCode); ?>" class="text" target="_new"><?php print($resConf); ?></a><?php endforeach; ?>
              </td>
           </tr>
          </table><br>
          </form>
          </center>
      <?php endif; ?>

     </div>
    </td>
  </tr>
</table>
</body>
</html>
<?php # If I remember well, the "exit" below has something to do with apd code tracing ?>
<?php exit; ?>
