<?php

//
//  Copyright (C) 2004-2011 by Autodesk, Inc.
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of version 2.1 of the GNU Lesser
//  General Public License as published by the Free Software Foundation.
//
//  This library 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
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with this library; if not, write to the Free Software
//  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
//

    require_once 'xmlparser.php';
    require_once 'constants.php';

    ////// Utility functions //////
    function CopyArray( $sourceArray, &$destArray )
    {
        $emptyArray = array();
        $destArray = array_merge( $sourceArray, $emptyArray );
    }

    function CleanUpPath( &$path )
    {
        $path = str_replace('\\', '/', $path );
    }

    function CleanUpMagicQuotes( &$path )
    {
        if ( get_magic_quotes_gpc() )
            $path = str_replace('\\\\', '\\', $path );
    }

    define( 'ID_SORT_COLUMN',               'id' );
    define( 'NAME_SORT_COLUMN',             'name' );
    define( 'DEFAULT_SORT_COLUMN',          ID_SORT_COLUMN );

    define( 'ASCENDING_SORT_DIRECTION',     'a' );
    define( 'DESCENDING_SORT_DIRECTION',    'd' );
    define( 'DEFAULT_SORT_DIRECTION',       ASCENDING_SORT_DIRECTION );

    function key_cmp_a( $a, $b )
    {
        $temp = strnatcasecmp( $a, $b );
        return $temp;
    }

    function key_cmp_d( $a, $b )
    {
        $temp = strnatcasecmp( $b, $a );
        return $temp;
    }

    function name_cmp_a( $a, $b )
    {
        $temp = strnatcasecmp( $a->name, $b->name );
        return $temp;
    }

    function name_cmp_d( $a, $b )
    {
        $temp = strnatcasecmp( $b->name, $a->name );
        return $temp;
    }

    function SortTable( &$table, $sortColumn, $sortDirection )
    {
        if ( $sortColumn == NULL || $sortDirection == NULL )
            return;

        // uasort and uksort are used here because tables are associative arrays and these two sort functions will maintain
        // the key for each item in the list.
        if ( $sortDirection === ASCENDING_SORT_DIRECTION )
            if ( $sortColumn === NAME_SORT_COLUMN )
                uasort( $table, 'name_cmp_a');
            else
                uksort ( $table, 'key_cmp_a' );
        else
            if ( $sortColumn === NAME_SORT_COLUMN )
                uasort( $table, 'name_cmp_d' );
            else
                uksort ( $table, 'key_cmp_d' );
    }

    function GetClientIp()
    {
        $clientIp = '';
        if (array_key_exists('HTTP_CLIENT_IP', $_SERVER)
            && strcasecmp($_SERVER['HTTP_CLIENT_IP'], 'unknown') != 0)
        {
            $clientIp = $_SERVER['HTTP_CLIENT_IP'];
        }
        else if (array_key_exists('HTTP_X_FORWARDED_FOR', $_SERVER)
            && strcasecmp($_SERVER['HTTP_X_FORWARDED_FOR'], 'unknown') != 0)
        {
            $clientIp = $_SERVER['HTTP_X_FORWARDED_FOR'];
        }
        else if (array_key_exists('REMOTE_ADDR', $_SERVER))
        {
            $clientIp = $_SERVER['REMOTE_ADDR'];
        }
        return $clientIp;
    }

    function GetClientAgent()
    {
        return "Map Admin";
    }

    ////// Users //////

    // User Table record type
    class UserTableRecord
    {
        var $name;
        var $description;
        var $groups;

        function UserTableRecord( $name, $description )
        {
            $this->name = $name;
            $this->description = $description;
            $this->groups = array();
        }
    }

    // User data is unicode data - use the appropriate parser
    function userRecParser( $text )
    {
        global $currKey;
        $currKey = "";

        // Process current element
        $name = getUnicodeValue( 'Name', $text );
        if ( empty( $name ) )
            return NULL;
        $fullName = getUnicodeValue( 'FullName', $text );
        $description = getUnicodeValue( 'Description', $text );

        $currKey = $name;
        return new UserTableRecord( $fullName, $description );
    }

    // Function which gets user data
    function GetUserDataFromDB( $targetGroup, $targetRole, &$targetTable )
    {
        global $site;
        global $userSortColumn;
        global $userSortDirection;

        // Call server to get comprehensive list of users.
        $targetTable = array();
        $users = $site->EnumerateUsers( $targetGroup, $targetRole, false );
        $buffer = "";
        $chunk = "";
        do {
            $chunkSize = $users->Read( $chunk, 4096 );
            $buffer = $buffer.$chunk;
        } while ( $chunkSize != 0 );

        // Parse XML in $buffer
        parseUnicodeData( $buffer, $targetTable, 'User', 'userRecParser' );

        SortTable( $targetTable, $userSortColumn, $userSortDirection );
    }

    $userSortDirection = DEFAULT_SORT_DIRECTION;
    function SetUserSortDirection( $sortDirection )
    {
        global $userSortDirection;

        $userSortDirection = $sortDirection;
    }

    $userSortColumm = NAME_SORT_COLUMN;
    function SetUserSortColumn( $sortColumn )
    {
        global $userSortColumn;

        $userSortColumn = $sortColumn;
    }

    $userData = NULL;
    function GetUserData()
    {
        global $userData;

        if ( $userData == NULL )
            GetUserDataFromDB( "", "", $userData );

        return $userData;
    }

    function GetUsers()
    {
        $users = GetUserData();
        return array_keys( $users );
    }

    function DeleteUser( $userID )
    {
        global $site;
        global $userData;

        // Validate
        $users = GetUserData();
        if ( empty( $userID ) || !array_key_exists( $userID, $users ) )
            return false;

        // Delete the user.
        $userToDelete = new MgStringCollection();
        $userToDelete->Add( $userID );
        $site->DeleteUsers( $userToDelete );

        // Update local userData array
        // Note that this is done rather than calling EnumerateUsers, again, because EnumerateUsers may take some time
        // to execute.
        $oldUserData = array();
        CopyArray( $userData, $oldUserData );
        $userData = array();
        foreach ( $oldUserData as $key => $val )
        {
            if ( $key != $userID )
                $userData[ $key ] = $val;
        }

        return true;
    }

    function LoadUserTable( &$targetTable, $firstIndex, $lastIndex, $includeGroups )
    {
        $targetTable = array();
        $users = GetUserData();

        $iUser = -1;
        foreach ( $users as $key => $val )
        {
            $iUser++;
            if ( $iUser < $firstIndex )
                continue;
            if ( $iUser > $lastIndex )
                break;

            $targetTable[ $key ] = $val;

            if ( $includeGroups )
                $val->groups = GetGroupMemberships( $key );
        }
    }

    function GetDataForUser( $userKey, $includeGroups )
    {
        $users = GetUserData();
        if ( !array_key_exists( $userKey, $users ) )
            return NULL;

        $userVal = $users[ $userKey ];
        if ( $includeGroups )
            $userVal->groups = GetGroupMemberships( $userKey );

        return $userVal;
    }

    function GetGroupMemberships( $userKey )
    {
        GetGroupDataFromDB( $userKey, "", $groupMemberships );
        $temp = array_keys ( $groupMemberships );

        // usort is used here because this is not an associative array and we need the keys to be reassigned; i.e. we want
        // $temp[0] to be the first item in the sorted list.
        usort( $temp, "strnatcasecmp" );

        return $temp;
    }

    ////// Groups //////

    // Group Table record type
    class GroupTableRecord
    {
        var $description;
        var $users;

        // Print contents
        function GroupTableRecord( $description )
        {
            $this->description = $description;
            $this->users = array();
        }
    }

    // Group data is unicode data - use the appropriate parser
    function groupRecParser( $text )
    {
        global $currKey;
        $currKey = "";

        // Process current element
        $name = getUnicodeValue( 'Name', $text );
        if ( empty( $name ) )
            return NULL;
        $description = getUnicodeValue( 'Description', $text );

        $currKey = $name;
        return new GroupTableRecord( $description );
    }

    $groupSortColumn = ID_SORT_COLUMN;
    $groupSortDirection = DEFAULT_SORT_DIRECTION;
    function SetGroupSortDirection( $sortDirection )
    {
        global $groupSortDirection;

        $groupSortDirection = $sortDirection;
    }

    function GetGroupDataFromDB( $targetUser, $targetRole, &$targetTable )
    {
        global $site;
        global $groupSortColumn;
        global $groupSortDirection;

        // Call server to get comprehensive list of users.
        $groups = $site->EnumerateGroups( $targetUser, $targetRole );
        $buffer = "";
        $chunk = "";
        do {
            $chunkSize = $groups->Read( $chunk, 4096 );
            $buffer = $buffer.$chunk;
        } while ( $chunkSize != 0 );

        // Parse XML in $buffer
        parseUnicodeData( $buffer, $targetTable, 'Group', 'groupRecParser' );

        SortTable( $targetTable, $groupSortColumn, $groupSortDirection );
    }

    $groupData = NULL;
    function GetGroupData()
    {
        global $groupData;

        if ( $groupData == NULL )
            GetGroupDataFromDB( "", "", $groupData );

        return $groupData;
    }

    function GetGroups()
    {
        $groups = GetGroupData();
        return array_keys( $groups );
    }

    function DeleteGroup( $groupName )
    {
        global $site;
        global $groupData;

        // Validate
        $groups = GetGroupData();
        if ( empty( $groupName ) || !array_key_exists( $groupName, $groups ) )
            return false;

        // Delete the group
        $groupToDelete = new MgStringCollection();
        $groupToDelete->Add( $groupName );
        $site->DeleteGroups( $groupToDelete );

        // Update local groupData array
        // Note that this is done rather than calling EnumerateGroups, again, because EnumerateGroups may take some time
        // to execute.
        $oldGroupData = array();
        CopyArray( $groupData, $oldGroupData );
        $groupData = array();
        foreach ( $oldGroupData as $key => $val )
        {
            if ( $key != $groupName )
                $groupData[ $key ] = $val;
        }

        return true;
    }

    function LoadGroupTable( &$targetTable, $firstIndex, $lastIndex, $includeUsers )
    {
        $targetTable = array();
        $groups = GetGroupData();

        $iGroup = -1;
        foreach ( $groups as $key => $val )
        {
            $iGroup++;
            if ( $iGroup < $firstIndex )
                continue;
            if ( $iGroup > $lastIndex )
                break;

            $targetTable[ $key ] = $val;

            if ( $includeUsers )
                $val->users = GetUserMembers( $key );
        }
    }

    function GetDataForGroup( $groupKey, $includeUsers )
    {
        $groups = GetGroupData();
        if ( !array_key_exists( $groupKey, $groups ) )
            return NULL;

        $groupVal = $groups[ $groupKey ];
        if ( $includeUsers )
            $groupVal->users = GetUserMembers( $groupKey );

        return $groupVal;
    }

    function GetUserMembers( $groupKey )
    {
        GetUserDataFromDB( $groupKey, "", $userMemberships );
        $temp = array_keys ( $userMemberships );

        // usort is used here because this is not an associative array and we need the keys to be reassigned; i.e. we want
        // $temp[0] to be the first item in the sorted list.
        usort( $temp, "strnatcasecmp" );

        return $temp;
    }

    ////// Roles //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    // Fixed roles for V1.0
    define( 'ADMIN_ROLE',  MgRole::Administrator );
    define( 'AUTHOR_ROLE', MgRole::Author );
    define( 'VIEWER_ROLE', MgRole::Viewer );

    // Function which builds a user table.
    function GetRoles( $targetUser, $targetGroup, &$targetList )
    {
        global $site;

        // Call server to get comprehensive list of roles.
        $roles = $site->EnumerateRoles( $targetUser, $targetGroup );

        // Build list
        $targetList = array();
        $numRoles = $roles->GetCount();
        for ( $i = 0; $i < $numRoles; $i++ )
        {
            $targetList[] = $roles->GetItem( $i );
        }
    }

    ////// UsersOrGroupsByRole //////////////////////////////////////////////////////////////////////////////////

    function GetUsersOrGroupsByRole( $bUser, $targetRole, &$targetList )
    {
        $targetTable = array();
        if ( $bUser )
            GetUserDataFromDB( "", $targetRole, $targetTable );
        else
            GetGroupDataFromDB( "", $targetRole, $targetTable );

        $targetList = array_keys( $targetTable );
    }

    ////// RolesTables ////////////////////////////////////////////////////////////////////////////////////////////////////

    // UserRoles Table record type
    class UserRolesTableRecord
    {
        var $name;
        var $adminOn;
        var $adminFromGroups;
        var $authorOn;
        var $authorFromGroups;
    }

    // Function which builds a userRoles table.
    function LoadUserRolesTable( &$targetTable, $firstIndex, $lastIndex )
    {
        GetUsersOrGroupsByRole( true, ADMIN_ROLE, $usersWithAdminRole );
        GetUsersOrGroupsByRole( false, ADMIN_ROLE, $groupsWithAdminRole );
        GetUsersOrGroupsByRole( true, AUTHOR_ROLE, $usersWithAuthorRole );
        GetUsersOrGroupsByRole( false, AUTHOR_ROLE, $groupsWithAuthorRole );

        $targetTable = array();
        LoadUserTable( $userTable, $firstIndex, $lastIndex, true );
        foreach ( $userTable as $userKey => $userVal )
        {
            // Create record for role data
            $userRolesRec = new UserRolesTableRecord();
            $targetTable[ $userKey ] = $userRolesRec;

            $userRolesRec->name = $userVal->name;

            if ( in_array( $userKey, $usersWithAdminRole ) )
                $userRolesRec->adminOn = true;
            else
                $userRolesRec->adminOn = false;

            $userRolesRec->adminFromGroups = array();
            $i = 0;
            foreach ( $userVal->groups as $groupKey )
            {
                if ( in_array( $groupKey, $groupsWithAdminRole ) )
                {
                    $userRolesRec->adminFromGroups[$i] = $groupKey;
                    $i++;
                }
            }

            if ( in_array( $userKey, $usersWithAuthorRole ) )
                $userRolesRec->authorOn = true;
            else
                $userRolesRec->authorOn = false;

            $userRolesRec->authorFromGroups = array();
            $i = 0;
            foreach ( $userVal->groups as $groupKey )
            {
                if ( in_array( $groupKey, $groupsWithAuthorRole ) )
                {
                    $userRolesRec->authorFromGroups[$i] = $groupKey;
                    $i++;
                }
            }
        }
    }

    // GroupRoles Table record type
    class GroupRolesTableRecord
    {
        var $adminOn;
        var $authorOn;
    }

    // Function which builds a userRoles table.
    function LoadGroupRolesTable( &$targetTable, $firstIndex, $lastIndex )
    {
        GetUsersOrGroupsByRole( false, ADMIN_ROLE, $groupsWithAdminRole );
        GetUsersOrGroupsByRole( false, AUTHOR_ROLE, $groupsWithAuthorRole );

        $targetTable = array();
        LoadGroupTable( $groupTable, $firstIndex, $lastIndex, false );
        foreach ( $groupTable as $groupKey => $groupVal )
        {
            // Create record for role data
            $groupRolesRec = new GroupRolesTableRecord();
            $targetTable[ $groupKey ] = $groupRolesRec;

            // For each role, record the group's role permission
            if ( in_array( $groupKey, $groupsWithAdminRole ) )
                $groupRolesRec->adminOn = true;
            else
                $groupRolesRec->adminOn = false;
            if ( in_array( $groupKey, $groupsWithAuthorRole ) )
                $groupRolesRec->authorOn = true;
            else
                $groupRolesRec->authorOn = false;
        }
    }

    ////// Servers //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    // Services
    define( 'CLIENT_SERVICE', "ClientService");
    define( 'DRAWING_SERVICE', "DrawingService");
    define( 'FEATURE_SERVICE', "FeatureService");
    define( 'MAPPING_SERVICE', "MappingService");
    define( 'RENDERING_SERVICE', "RenderingService");
    define( 'TILE_SERVICE', "TileService");
    define( 'RESOURCE_SERVICE', "ResourceService");
    define( 'SITE_SERVICE', "SiteService");

    $allPossibleSiteServerServices = array();
    $allPossibleSiteServerServices[0] = CLIENT_SERVICE;
    $allPossibleSiteServerServices[1] = DRAWING_SERVICE;
    $allPossibleSiteServerServices[2] = FEATURE_SERVICE;
    $allPossibleSiteServerServices[3] = MAPPING_SERVICE;
    $allPossibleSiteServerServices[4] = RENDERING_SERVICE;
    $allPossibleSiteServerServices[5] = RESOURCE_SERVICE;
    $allPossibleSiteServerServices[6] = SITE_SERVICE;
    $allPossibleSiteServerServices[7] = TILE_SERVICE;

    $allPossibleSupportServerServices = array();
    $allPossibleSupportServerServices[0] = MAPPING_SERVICE;
    $allPossibleSupportServerServices[1] = RENDERING_SERVICE;
    $allPossibleSupportServerServices[2] = TILE_SERVICE;

    // Server Table record type
    class ServerTableRecord
    {
        var $name;
        var $description;
        var $poweredUp;
        var $version;
        var $online;
        var $services;

        function ServerTableRecord( $name, $description )
        {
            $this->name = $name;
            $this->description = $description;
            $this->poweredUp = false;
            $this->version = "";
            $this->online = false;
            $this->services = array();
            $this->services[ DRAWING_SERVICE ] = false;
            $this->services[ FEATURE_SERVICE ] = false;
            $this->services[ MAPPING_SERVICE ] = false;
            $this->services[ RENDERING_SERVICE ] = false;
            $this->services[ TILE_SERVICE ] = false;
        }
    }

    // The server list is unicode data - use appropriate parser
    function serverRecParser( $text )
    {
        global $currKey;
        $currKey = "";

        // Process current element
        $ipAddress = getUnicodeValue( 'IpAddress', $text );
        if ( empty( $ipAddress ) )
            return NULL;
        $name = getUnicodeValue( 'Name', $text );
        $description = getUnicodeValue( 'Description', $text );

        $currKey = $ipAddress;
        return new ServerTableRecord( $name, $description );
    }

    function GetServerDataFromDB( &$targetTable )
    {
        global $site;
        global $userInfo;

        $targetTable =  array();

        // Put Site Server in $targetTable.
        $serverRec = new ServerTableRecord( "Site Server", "" );
        $serverKey = $site->GetCurrentSiteAddress();
        $targetTable[ $serverKey ] = $serverRec;

        $dbData = $site->EnumerateServers();
        $buffer = "";
        $chunk = "";
        do {
            $chunkSize = $dbData->Read( $chunk, 4096 );
            $buffer = $buffer.$chunk;
        } while ( $chunkSize != 0 );

        // Parse XML in $buffer
        parseUnicodeData( $buffer, $supportServers, 'Server', 'serverRecParser' );

        // Add support servers to target table.
        foreach( $supportServers as $key => $val )
            $targetTable[ $key ] = $val;

        // Get values for Online and Services fields
        foreach ( $targetTable as $key => $val )
        {
            try
            {
                $serverAdmin = new MgServerAdmin();
                $serverAdmin->Open( $key, $userInfo );
                $val->services = GetOptionalServerServices( $serverAdmin );
                $val->online = GetOnline( $serverAdmin );
                $val->version = GetVersion( $serverAdmin );
                $val->poweredUp = true;
            }
            catch ( MgException $e )
            {
                $val->poweredUp = false;
            }
            if ( $serverAdmin != NULL )
                $serverAdmin->Close();
        }
    }

    $serverData = NULL;
    function GetServerData()
    {
        global $serverData;

        if ( $serverData == NULL )
            GetServerDataFromDB( $serverData );

        return $serverData;
    }

    function ReloadServerData()
    {
        global $serverData;

        GetServerDataFromDB( $serverData );
    }

    function GetServers()
    {
        $servers = GetServerData();
        return array_keys( $servers );
    }

    function GetServerName( $serverAddress )
    {
        $name = "";

        $servers = GetServerData();
        if ( array_key_exists( $serverAddress, $servers ) )
            $name = $servers[ $serverAddress ]->name;

        return $name;
    }

    function GetServerCount()
    {
        $servers = GetServerData();
        return sizeof( $servers );
    }

    function DeleteServer( $serverAddress )
    {
        global $site;
        global $serverData;

        // Validate
        $servers = GetServerData();
        if ( empty( $serverAddress ) || !array_key_exists( $serverAddress, $servers ) )
            return false;

        // Delete the server.
        $serverRec = GetDataForServer( $serverAddress );
        if ( $serverRec == NULL )
            return false;
        $serverName = $serverRec->name;
        $site->RemoveServer( $serverName );

        // Update local serverData array
        // Note that this is done rather than calling EnumerateServers, again, because EnumerateServers may take some time
        // to execute.
        $oldServerData = array();
        CopyArray( $serverData, $oldServerData );
        $serverData = array();
        foreach ( $oldServerData as $key => $val )
        {
            if ( $key != $serverAddress )
                $serverData[ $key ] = $val;
        }

        return true;
    }

    function LoadServerTable( &$targetTable, $firstIndex, $lastIndex )
    {
        $targetTable = array();
        $servers = GetServerData();

        $iServer = -1;
        foreach ( $servers as $key => $val )
        {
            $iServer++;
            if ( $iServer < $firstIndex )
                continue;
            if ( $iServer > $lastIndex )
                break;

            $targetTable[ $key ] = $val;
        }
    }

    function GetDataForServer( $serverKey )
    {
        $servers = GetServerData();
        if ( !array_key_exists( $serverKey, $servers ) )
            return NULL;
        else
            return $servers[ $serverKey ];
    }

    function GetOptionalServerServices( $serverAdmin )
    {
        $services = array();
        $services[ DRAWING_SERVICE   ] = false;
        $services[ FEATURE_SERVICE   ] = false;
        $services[ MAPPING_SERVICE   ] = false;
        $services[ RENDERING_SERVICE ] = false;
        $services[ TILE_SERVICE      ] = false;

        $props = $serverAdmin->GetConfigurationProperties( MgConfigProperties::HostPropertiesSection );

        $prop = $props->GetItem( MgConfigProperties::HostPropertyDrawingService );
        if ( $prop->GetValue() == "1" )
            $services[ DRAWING_SERVICE ] = true;

        $prop = $props->GetItem( MgConfigProperties::HostPropertyFeatureService );
        if ( $prop->GetValue() == "1" )
            $services[ FEATURE_SERVICE ] = true;

        $prop = $props->GetItem( MgConfigProperties::HostPropertyMappingService );
        if ( $prop->GetValue() == "1" )
            $services[ MAPPING_SERVICE ] = true;

        $prop = $props->GetItem( MgConfigProperties::HostPropertyRenderingService );
        if ( $prop->GetValue() == "1" )
            $services[ RENDERING_SERVICE ] = true;

        $prop = $props->GetItem( MgConfigProperties::HostPropertyTileService );
        if ( $prop->GetValue() == "1" )
            $services[ TILE_SERVICE ] = true;

        return $services;
    }

    function SetOptionalServerServices( $serverAdmin, $services )
    {
        $props = new MgPropertyCollection();

         if ( $services[ DRAWING_SERVICE ] )
            $propVal = "1";
        else
            $propVal = "0";
        $prop = new MgStringProperty( MgConfigProperties::HostPropertyDrawingService, $propVal );
        $props->Add( $prop );

         if ( $services[ FEATURE_SERVICE ] )
            $propVal = "1";
        else
            $propVal = "0";
        $prop = new MgStringProperty( MgConfigProperties::HostPropertyFeatureService, $propVal );
        $props->Add( $prop );

         if ( $services[ MAPPING_SERVICE ] )
            $propVal = "1";
        else
            $propVal = "0";
        $prop = new MgStringProperty( MgConfigProperties::HostPropertyMappingService, $propVal );
        $props->Add( $prop );

         if ( $services[ RENDERING_SERVICE ] )
            $propVal = "1";
        else
            $propVal = "0";
        $prop = new MgStringProperty( MgConfigProperties::HostPropertyRenderingService, $propVal );
        $props->Add( $prop );

         if ( $services[ TILE_SERVICE ] )
            $propVal = "1";
        else
            $propVal = "0";
        $prop = new MgStringProperty( MgConfigProperties::HostPropertyTileService, $propVal );
        $props->Add( $prop );

        // Disable Service manipulation from Site Admin
        // $serverAdmin->SetConfigurationProperties( MgConfigProperties::HostPropertiesSection, $props );
    }

    function GetOnline( $serverAdmin )
    {
        return $serverAdmin->IsOnline();
    }

    function SetOnline( $serverAdmin, $online )
    {
        if ( $online )
            $serverAdmin->BringOnline();
        else
            $serverAdmin->TakeOffline();
    }

    function GetVersion( $serverAdmin )
    {
        $infoProps = $serverAdmin->GetInformationProperties();
        $versionProp = $infoProps->GetItem( MgServerInformationProperties::ServerVersion );
        return $versionProp->GetValue();
    }

    function GetUnmanagedDataMappings( $serverAdmin )
    {
        $mappings = array();
        $props = $serverAdmin->GetConfigurationProperties( MgConfigProperties::UnmanagedDataMappingsSection );

        for ( $i = 0; $i < $props->GetCount(); $i++)
        {
            $prop = $props->GetItem($i);
            $mappings[$prop->GetName()] = $prop->GetValue();
        }

        return $mappings;
    }

    // if $mappingName already exists, then that mapping is updated
    // if $mappingName does NOT exist, it is added
    function SetUnmanagedDataMapping( $serverAdmin, $mappingName, $location )
    {
        $props = new MgPropertyCollection();
        $prop = new MgStringProperty( $mappingName, $location );

        $props->Add( $prop );

        $serverAdmin->SetConfigurationProperties( MgConfigProperties::UnmanagedDataMappingsSection, $props );
    }

    function DeleteUnmanagedDataMapping( $serverAdmin, $mappingName )
    {
        $success = false;

        $props = new MgPropertyCollection();
        $prop = new MgStringProperty( $mappingName, "" ); // value doesn't matter
        $props->Add( $prop );

        $serverAdmin->RemoveConfigurationProperties( MgConfigProperties::UnmanagedDataMappingsSection, $props );
        $success = true;

        return $success;
    }

    function LoadMappingTable( &$targetTable, $table, $firstIndex, $lastIndex )
    {
        $targetTable = array();

        $iMapping = -1;
        foreach ( $table as $key => $val )
        {
            $iMapping++;
            if ( $iMapping < $firstIndex )
                continue;
            if ( $iMapping > $lastIndex )
                break;

            $targetTable[ $key ] = $val;
        }
    }


    ////// Properties //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    class GeneralPropsRecord
    {
        var $displayName;
        var $displayNamePropStr;
        var $locale;
        var $localePropStr;
        var $defaultMessageLocale;
        var $defaultMessageLocalePropStr;
        var $localizationResourcesPath;
        var $localizationResourcesPathPropStr;
        var $tcpIpMtu;
        var $tcpIpMtuPropStr;
        var $tempPath;
        var $tempPathPropStr;
        var $fdoPath;
        var $fdoPathPropStr;
        var $email;
        var $emailPropStr;
        var $connectionTimeOut;
        var $connectionTimeOutPropStr;
        var $connectionTimerInt;
        var $connectionTimerIntPropStr;

        function GeneralPropsRecord()
        {
            $this->displayName = "";
            $this->displayNamePropStr = MgConfigProperties::GeneralPropertyDisplayName;
            $this->locale = "";
            $this->localePropStr = MgConfigProperties::GeneralPropertyLocale;
            $this->defaultMessageLocale = "";
            $this->defaultMessageLocalePropStr = MgConfigProperties::GeneralPropertyDefaultMessageLocale;
            $this->localizationResourcesPath = "";
            $this->localizationResourcesPathPropStr = MgConfigProperties::GeneralPropertyResourcesPath;
            $this->tcpIpMtu = "";
            $this->tcpIpMtuPropStr = MgConfigProperties::GeneralPropertyTcpIpMtu;
            $this->tempPath = "";
            $this->tempPathPropStr = MgConfigProperties::GeneralPropertyTempPath;
            $this->fdoPath = "";
            $this->fdoPathPropStr = MgConfigProperties::GeneralPropertyFdoPath;
            $this->email = "";
            $this->emailPropStr = MgConfigProperties::AdministrativeConnectionPropertyEmail;
            $this->connectionTimeOut = 0;
            $this->connectionTimeOutPropStr = MgConfigProperties::GeneralPropertyConnectionTimeout;
            $this->connectionTimerInt = 0;
            $this->connectionTimerIntPropStr = MgConfigProperties::GeneralPropertyConnectionTimerInterval;
        }

        function GetProps( $serverAdmin )
        {
            $props = $serverAdmin->GetConfigurationProperties( MgConfigProperties::GeneralPropertiesSection );

            $prop = $props->GetItem( $this->displayNamePropStr );
            $this->displayName = $prop->GetValue();

            $prop = $props->GetItem( $this->localePropStr );
            $this->locale = $prop->GetValue();

            $prop = $props->GetItem( $this->defaultMessageLocalePropStr );
            $this->defaultMessageLocale = $prop->GetValue();

            $prop = $props->GetItem( $this->localizationResourcesPathPropStr );
            $this->localizationResourcesPath = $prop->GetValue();
            CleanUpPath( $this->localizationResourcesPath );

            $prop = $props->GetItem( $this->tcpIpMtuPropStr );
            $this->tcpIpMtu = $prop->GetValue();

            $prop = $props->GetItem( $this->tempPathPropStr );
            $this->tempPath = $prop->GetValue();
            CleanUpPath( $this->tempPath );

            $prop = $props->GetItem( $this->fdoPathPropStr );
            $this->fdoPath = $prop->GetValue();
            CleanUpPath( $this->fdoPath );

            $prop = $props->GetItem( $this->connectionTimeOutPropStr );
            $this->connectionTimeOut = $prop->GetValue();

            $prop = $props->GetItem( $this->connectionTimerIntPropStr );
            $this->connectionTimerInt = $prop->GetValue();

            $props = $serverAdmin->GetConfigurationProperties( MgConfigProperties::AdministrativeConnectionPropertiesSection );
            $prop = $props->GetItem( $this->emailPropStr );
            $this->email = $prop->GetValue();
        }

        function ValidateProps()
        {
            global $errInvalidServerDefaultLocale;
            global $errInvalidServerLocalizationResourcesPath;
            global $errInvalidServerTcpIpMtu;
            global $errInvalidServerTempPath;
            global $errInvalidServerFdoPath;
            global $errInvalidConnectionTimeOut;
            global $errInvalidConnectionTimerInt;
            global $errInvalidServiceRegistrationTimerInt;

            if ( empty( $this->defaultMessageLocale ) )
                throw new Exception( $errInvalidServerDefaultLocale );
            if ( empty( $this->localizationResourcesPath ) )
                throw new Exception( $errInvalidServerLocalizationResourcesPath );
            if ( $this->tcpIpMtu < 0 )
                throw new Exception( $errInvalidServerTcpIpMtu );
            if ( empty( $this->tempPath ) )
                throw new Exception( $errInvalidServerTempPath );
            if ( empty( $this->fdoPath ) )
                throw new Exception( $errInvalidServerFdoPath );
            if ( $this->connectionTimeOut <= 0  )
                throw new Exception( $errInvalidConnectionTimeOut );
            if ( $this->connectionTimerInt < 0 )
                throw new Exception( $errInvalidConnectionTimerInt );
        }

        function SetProps( $serverAdmin )
        {
            $props = new MgPropertyCollection();

            $prop = new MgStringProperty( $this->displayNamePropStr , $this->displayName );
            $props->Add( $prop );

            $prop = new MgStringProperty( $this->localePropStr , $this->locale );
            $props->Add( $prop );

            $prop = new MgStringProperty( $this->defaultMessageLocalePropStr , $this->defaultMessageLocale );
            $props->Add( $prop );

            $prop = new MgStringProperty( $this->localizationResourcesPathPropStr, $this->localizationResourcesPath );
            $props->Add( $prop );

            $prop = new MgStringProperty( $this->tcpIpMtuPropStr, $this->tcpIpMtu );
            $props->Add( $prop );

            $prop = new MgStringProperty( $this->tempPathPropStr, $this->tempPath );
            $props->Add( $prop );

            $prop = new MgStringProperty( $this->fdoPathPropStr, $this->fdoPath );
            $props->Add( $prop );

            $prop = new MgStringProperty( $this->connectionTimeOutPropStr, $this->connectionTimeOut );
            $props->Add( $prop );

            $prop = new MgStringProperty( $this->connectionTimerIntPropStr, $this->connectionTimerInt );
            $props->Add( $prop );

            $serverAdmin->SetConfigurationProperties( MgConfigProperties::GeneralPropertiesSection, $props );

            $props->Clear();
            $prop = new MgStringProperty( $this->emailPropStr, $this->email );
            $props->Add( $prop );
            $serverAdmin->SetConfigurationProperties( MgConfigProperties::AdministrativeConnectionPropertiesSection, $props );
        }
    }

    class ConnectionPropsRecord
    {
         var $connStr;
         var $propSectionStr;
         var $maxConns;
         var $maxConnsPropStr;
         var $port;
         var $portPropStr;
         var $queueSize;
         var $queueSizePropStr;
         var $threadPoolSize;
         var $threadPoolSizePropStr;

        function ConnectionPropsRecord( $connStr, $propSectionStr, $maxConnsPropStr, $portPropStr, $queueSizePropStr, $threadPoolSizePropStr )
        {
            $this->connStr = $connStr;
            $this->propSectionStr = $propSectionStr;
            $this->maxConns = 0;
            $this->maxConnsPropStr = $maxConnsPropStr;
            $this->port = 0;
            $this->portPropStr = $portPropStr;
            $this->queueSize = 0;
            $this->queueSizePropStr = $queueSizePropStr;
            $this->threadPoolSize = 0;
            $this->threadPoolSizePropStr = $threadPoolSizePropStr;
        }

        function GetProps( $serverAdmin )
        {
            $props = $serverAdmin->GetConfigurationProperties( $this->propSectionStr );

            $prop = $props->GetItem( $this->maxConnsPropStr );
            $this->maxConns = $prop->GetValue();

            $prop = $props->GetItem( $this->portPropStr );
            $this->port = $prop->GetValue();

            $prop = $props->GetItem( $this->queueSizePropStr );
            $this->queueSize = $prop->GetValue();

            $prop = $props->GetItem( $this->threadPoolSizePropStr );
            $this->threadPoolSize = $prop->GetValue();
        }

        function ValidateProps()
        {
            global $errInvalidServerMaxConns;
            global $errInvalidServerConnQueueSize;
            global $errInvalidServerConnThreadPoolSize;

            if ( $this->maxConns < 1 )
                throw new Exception( sprintf( $errInvalidServerMaxConns, $this->connStr ) );
            if ( $this->queueSize < 1 )
                throw new Exception( sprintf( $errInvalidServerConnQueueSize, $this->connStr ) );
            if ( $this->threadPoolSize < 1 )
                throw new Exception( sprintf( $errInvalidServerConnThreadPoolSize, $this->connStr ) );
        }

        function SetProps( $serverAdmin )
        {
            $props = new MgPropertyCollection();

            $prop = new MgStringProperty( $this->maxConnsPropStr, $this->maxConns );
            $props->Add( $prop );

            $prop = new MgStringProperty( $this->portPropStr, $this->port );
            $props->Add( $prop );

            $prop = new MgStringProperty( $this->queueSizePropStr, $this->queueSize  );
            $props->Add( $prop );

            $prop = new MgStringProperty( $this->threadPoolSizePropStr, $this->threadPoolSize );
            $props->Add( $prop );

            $serverAdmin->SetConfigurationProperties( $this->propSectionStr, $props );
        }
    }

    class FeatureServicePropsRecord
    {
        var $enabled;
        var $dataConnTimeOut;
        var $dataConnTimeOutPropStr;
        var $dataConnTimerInt;
        var $dataConnTimerIntPropStr;
        var $dataConnPoolSize;
        var $dataConnPoolSizePropStr;
        var $dataConnPoolSizeCustom;
        var $dataConnPoolSizeCustomPropStr;
        var $dataCacheSize;
        var $dataCacheSizePropStr;

        function FeatureServicePropsRecord()
        {
            $this->enabled = true;
            $this->enabledID = "featureServiceEnabled";
            $this->dataConnPoolSize = 1;
            $this->dataConnPoolSizePropStr = MgConfigProperties::FeatureServicePropertyDataConnectionPoolSize;
            $this->dataConnPoolSizeID = "featureDataConnPoolSize";
            $this->dataConnPoolSizeCustom = "";
            $this->dataConnPoolSizeCustomPropStr = MgConfigProperties::FeatureServicePropertyDataConnectionPoolSizeCustom;
            $this->dataConnPoolSizeCustomID = "featureDataConnPoolSizeCustom";
            $this->dataConnTimeOut = 0;
            $this->dataConnTimeOutPropStr = MgConfigProperties::FeatureServicePropertyDataConnectionTimeout;
            $this->dataConnTimeOutID = "featureDataConnTimeOut";
            $this->dataConnTimerInt = 0;
            $this->dataConnTimerIntPropStr = MgConfigProperties::FeatureServicePropertyDataConnectionTimerInterval;
            $this->dataConnTimerIntID = "featureDataConnTimerInt";
            $this->dataCacheSize = 0;
            $this->dataCacheSizePropStr = MgConfigProperties::FeatureServicePropertyDataCacheSize;
            $this->dataCacheSizeID = "featureDataConnCacheSize";
        }

        function GetProps( $serverAdmin )
        {
            $props = $serverAdmin->GetConfigurationProperties( MgConfigProperties::FeatureServicePropertiesSection );

            $prop = $props->GetItem( $this->dataConnPoolSizePropStr );
            $this->dataConnPoolSize = $prop->GetValue();

            $prop = $props->GetItem( $this->dataConnPoolSizeCustomPropStr );
            $this->dataConnPoolSizeCustom = $prop->GetValue();

            $prop = $props->GetItem( $this->dataConnTimeOutPropStr );
            $this->dataConnTimeOut = $prop->GetValue();

            $prop = $props->GetItem( $this->dataConnTimerIntPropStr );
            $this->dataConnTimerInt = $prop->GetValue();

            $prop = $props->GetItem( $this->dataCacheSizePropStr );
            $this->dataCacheSize = $prop->GetValue();
        }

        function ValidateProps()
        {
            global $errInvalidFeatureDataConnPoolSize;
            global $errInvalidFeatureDataConnPoolSizeCustom;
            global $errInvalidFeatureDataConnTimeOut;
            global $errInvalidFeatureDataConnTimerInt;
            global $errInvalidFeatureDataCacheSize;

            if ( $this->dataConnPoolSize <= 0 )
                throw new Exception( $errInvalidFeatureDataConnPoolSize );
            if ( $this->dataConnTimeOut <= 0 )
                throw new Exception( $errInvalidFeatureDataConnTimeOut );
            if ( $this->dataConnTimerInt < 0 )
                throw new Exception( $errInvalidFeatureDataConnTimerInt );
            if ( $this->dataCacheSize < 1 )
                throw new Exception( $errInvalidFeatureDataCacheSize );
        }

        function SetProps( $serverAdmin )
        {
            $props = new MgPropertyCollection();

            $prop = new MgStringProperty( $this->dataConnPoolSizePropStr, $this->dataConnPoolSize );
            $props->Add( $prop );

            $prop = new MgStringProperty( $this->dataConnPoolSizeCustomPropStr, $this->dataConnPoolSizeCustom );
            $props->Add( $prop );

            $prop = new MgStringProperty( $this->dataConnTimeOutPropStr, $this->dataConnTimeOut );
            $props->Add( $prop );

            $prop = new MgStringProperty( $this->dataConnTimerIntPropStr, $this->dataConnTimerInt );
            $props->Add( $prop );

            $prop = new MgStringProperty( $this->dataCacheSizePropStr, $this->dataCacheSize );
            $props->Add( $prop );

            $serverAdmin->SetConfigurationProperties( MgConfigProperties::FeatureServicePropertiesSection, $props );
        }
    }

    class ResourceServicePropsRecord
    {
        var $enabled;
        var $dataFileTrashFolder;
        var $dataFileTrashFolderPropStr;
        var $libraryDataFileFolder;
        var $libraryDataFileFolderPropStr;
        var $libraryRepositoryFolder;
        var $libraryRepositoryFolderPropStr;
        var $sessionDataFileFolder;
        var $sessionDataFileFolderPropStr;
        var $sessionRepositoryFolder;
        var $sessionRepositoryFolderPropStr;
        var $siteRepositoryFolder;
        var $siteRepositoryFolderPropStr;
        var $resourceSchemaFolder;
        var $resourceSchemaFolderPropStr;
        var $repositoryCheckpointsTimerInterval;
        var $repositoryCheckpointsTimerIntervalPropStr;
        var $packagesPath;
        var $packagesPathPropStr;


        function ResourceServicePropsRecord()
        {
            $this->enabled = true;
            $this->dataFileTrashFolder = "";
            $this->dataFileTrashFolderPropStr = MgConfigProperties::ResourceServicePropertyResourceDataFileTrashFolderName;
            $this->libraryDataFileFolder = "";
            $this->libraryDataFileFolderPropStr = MgConfigProperties::ResourceServicePropertyLibraryResourceDataFilePath;
            $this->libraryRepositoryFolder = "";
            $this->libraryRepositoryFolderPropStr = MgConfigProperties::ResourceServicePropertyLibraryRepositoryPath;
            $this->sessionDataFileFolder = "";
            $this->sessionDataFileFolderPropStr = MgConfigProperties::ResourceServicePropertySessionResourceDataFilePath;
            $this->sessionRepositoryFolder = "";
            $this->sessionRepositoryFolderPropStr = MgConfigProperties::ResourceServicePropertySessionRepositoryPath;
            $this->siteRepositoryFolder = "";
            $this->siteRepositoryFolderPropStr = MgConfigProperties::ResourceServicePropertySiteRepositoryPath;
            $this->resourceSchemaFolder = "";
            $this->resourceSchemaFolderPropStr = MgConfigProperties::ResourceServicePropertyResourceSchemaFilePath;
            $this->repositoryCheckpointsTimerInterval = 0;
            $this->repositoryCheckpointsTimerIntervalPropStr = MgConfigProperties::ResourceServicePropertyRepositoryCheckpointsTimerInterval;
            $this->packagesPath = "";
            $this->packagesPathPropStr = MgConfigProperties::ResourceServicePropertyPackagesPath;
        }

        function GetProps( $serverAdmin )
        {
            $props = $serverAdmin->GetConfigurationProperties( MgConfigProperties::ResourceServicePropertiesSection );

            $prop = $props->GetItem( $this->dataFileTrashFolderPropStr );
            $this->dataFileTrashFolder = $prop->GetValue();
            CleanUpPath( $this->dataFileTrashFolder );

            $prop = $props->GetItem( $this->libraryDataFileFolderPropStr );
            $this->libraryDataFileFolder = $prop->GetValue();
            CleanUpPath( $this->libraryDataFileFolder );

            $prop = $props->GetItem( $this->libraryRepositoryFolderPropStr );
            $this->libraryRepositoryFolder = $prop->GetValue();
            CleanUpPath( $this->libraryRepositoryFolder );

            $prop = $props->GetItem( $this->sessionDataFileFolderPropStr );
            $this->sessionDataFileFolder = $prop->GetValue();
            CleanUpPath( $this->sessionDataFileFolder );

            $prop = $props->GetItem( $this->sessionRepositoryFolderPropStr );
            $this->sessionRepositoryFolder = $prop->GetValue();
            CleanUpPath( $this->sessionRepositoryFolder );

            $prop = $props->GetItem( $this->siteRepositoryFolderPropStr );
            $this->siteRepositoryFolder = $prop->GetValue();
            CleanUpPath( $this->siteRepositoryFolder );

            $prop = $props->GetItem( $this->resourceSchemaFolderPropStr );
            $this->resourceSchemaFolder = $prop->GetValue();
            CleanUpPath( $this->resourceSchemaFolder );

            $prop = $props->GetItem( $this->repositoryCheckpointsTimerIntervalPropStr );
            $this->repositoryCheckpointsTimerInterval = $prop->GetValue();

            $prop = $props->GetItem( $this->packagesPathPropStr );
            $this->packagesPath = $prop->GetValue();
            CleanUpPath( $this->packagesPath );

            $props = $serverAdmin->GetConfigurationProperties( MgConfigProperties::GeneralPropertiesSection );

        }

        function ValidateProps()
        {
            global $errInvalidResourceDataFileTrashFolder;
            global $errInvalidResourceLibraryDataFileFolder;
            global $errInvalidResourceLibraryRepositoryFolder;
            global $errInvalidResourceSessionDataFileFolder;
            global $errInvalidResourceSessionRepositoryFolder;
            global $errInvalidResourceSiteRepositoryFolder;
            global $errInvalidResourceResourceSchemaFolder;
            global $errInvalidRepositoryCheckpointsTimerInterval;
            global $errInvalidPackagesPath;

            if ( empty( $this->dataFileTrashFolder ) )
                throw new Exception( $errInvalidResourceDataFileTrashFolder );
            if ( empty ($this->libraryDataFileFolder) )
                throw new Exception( $errInvalidResourceLibraryDataFileFolder );
            if ( empty ($this->libraryRepositoryFolder) )
                throw new Exception( $errInvalidResourceLibraryRepositoryFolder );
            if ( empty( $this->sessionDataFileFolder ) )
                throw new Exception( $errInvalidResourceSessionDataFileFolder );
            if ( empty( $this->sessionRepositoryFolder ) )
                throw new Exception( $errInvalidResourceSessionRepositoryFolder );
            if ( empty( $this->siteRepositoryFolder ) )
                throw new Exception( $errInvalidResourceSiteRepositoryFolder );
            if ( empty( $this->resourceSchemaFolder ) )
                throw new Exception( $errInvalidResourceResourceSchemaFolder );
            if ( $this->repositoryCheckpointsTimerInterval < 0 )
                throw new Exception( $errInvalidRepositoryCheckpointsTimerInterval );
            if ( empty( $this->packagesPath ) )
                throw new Exception( $errInvalidPackagesPath );
        }

        function SetProps( $serverAdmin )
        {
            $props = new MgPropertyCollection();

            $prop = new MgStringProperty( $this->dataFileTrashFolderPropStr, $this->dataFileTrashFolder );
            $props->Add( $prop );

            $prop = new MgStringProperty( $this->libraryDataFileFolderPropStr, $this->libraryDataFileFolder );
            $props->Add( $prop );

            $prop = new MgStringProperty( $this->libraryRepositoryFolderPropStr, $this->libraryRepositoryFolder );
            $props->Add( $prop );

            $prop = new MgStringProperty( $this->sessionDataFileFolderPropStr, $this->sessionDataFileFolder );
            $props->Add( $prop );

            $prop = new MgStringProperty( $this->sessionRepositoryFolderPropStr, $this->sessionRepositoryFolder );
            $props->Add( $prop );

            $prop = new MgStringProperty( $this->siteRepositoryFolderPropStr, $this->siteRepositoryFolder );
            $props->Add( $prop );

            $prop = new MgStringProperty( $this->resourceSchemaFolderPropStr, $this->resourceSchemaFolder );
            $props->Add( $prop );

            $prop = new MgStringProperty( $this->repositoryCheckpointsTimerIntervalPropStr, $this->repositoryCheckpointsTimerInterval );
            $props->Add( $prop );

            $prop = new MgStringProperty( $this->packagesPathPropStr, $this->packagesPath );
            $props->Add( $prop );

            $serverAdmin->SetConfigurationProperties( MgConfigProperties::ResourceServicePropertiesSection, $props );

        }

    }


    class TileServicePropsRecord
    {
        var $enabled;
        var $tileCachePath;
        var $tileCachePathPropStr;

        function TileServicePropsRecord()
        {
            $this->enabled = true;
            $this->tileCachePath = "";
            $this->tileCachePathPropStr = MgConfigProperties::TileServicePropertyTileCachePath;
        }

        function GetProps( $serverAdmin )
        {
            $props = $serverAdmin->GetConfigurationProperties( MgConfigProperties::TileServicePropertiesSection );

            $prop = $props->GetItem( $this->tileCachePathPropStr );
            $this->tileCachePath = $prop->GetValue();
            CleanUpPath( $this->tileCachePath );
        }

        function ValidateProps()
        {
            global $errInvalidTileCachePath;

            if ( empty( $this->tileCachePath ) )
                throw new Exception( $errInvalidTileCachePath );
        }

        function SetProps( $serverAdmin )
        {
            $props = new MgPropertyCollection();

            $prop = new MgStringProperty( $this->tileCachePathPropStr, $this->tileCachePath );
            $props->Add( $prop );

            $serverAdmin->SetConfigurationProperties( MgConfigProperties::TileServicePropertiesSection, $props );
        }
    }



    class SiteServicePropsRecord
    {
        var $enabled;
        var $sessionTimeout;
        var $sessionTimeoutPropStr;
        var $sessionTimerInt;
        var $sessionTimerIntPropStr;

        function SiteServicePropsRecord()
        {
            $this->enabled = true;
            $this->sessionTimeout = 0;
            $this->sessionTimeoutPropStr = MgConfigProperties::SiteServicePropertySessionTimeout;
            $this->sessionTimerInt = 0;
            $this->sessionTimerIntPropStr = MgConfigProperties::SiteServicePropertySessionTimerInterval;
        }

        function GetProps( $serverAdmin )
        {
            $props = $serverAdmin->GetConfigurationProperties( MgConfigProperties::SiteServicePropertiesSection );

            $prop = $props->GetItem( $this->sessionTimeoutPropStr );
            $this->sessionTimeout = $prop->GetValue();

            $prop = $props->GetItem( $this->sessionTimerIntPropStr );
            $this->sessionTimerInt = $prop->GetValue();
        }

        function ValidateProps()
        {
            global $errInvalidSessionTimeOut;
            global $errInvalidSessionTimerInt;

            if ( $this->sessionTimeout <= 0 )
                throw new Exception( $errInvalidSessionTimeOut );
            if ( $this->sessionTimerInt <= 0 )
                throw new Exception( $errInvalidSessionTimerInt );
        }

        function SetProps( $serverAdmin )
        {
            $props = new MgPropertyCollection();

            $prop = new MgStringProperty( $this->sessionTimeoutPropStr, $this->sessionTimeout );
            $props->Add( $prop );

            $prop = new MgStringProperty( $this->sessionTimerIntPropStr, $this->sessionTimerInt );
            $props->Add( $prop );

            $serverAdmin->SetConfigurationProperties( MgConfigProperties::SiteServicePropertiesSection, $props );
        }
    }


    ////// Logs //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    // Logs
    define( 'ACTIVE_LOGS',            "ActiveLogs" );
    define( 'ACCESS_LOG',             MgLogFileType::Access );
    define( 'ADMIN_LOG',             MgLogFileType::Admin );
    define( 'AUTHENTICATION_LOG',      MgLogFileType::Authentication );
    define( 'ERROR_LOG',             MgLogFileType::Error );
    define( 'SESSION_LOG',             MgLogFileType::Session );
    define( 'TRACE_LOG',             MgLogFileType::Trace );

    define( 'COMMA_DELIMITER',        "Comma" );
    define( 'TAB_DELIMITER',        "Tab" );
    define( 'SPACE_DELIMITER',         "Space" );
    define( 'NO_DELIMITER',            "None" );
    $logDelimiters = array();
    $logDelimiters[ COMMA_DELIMITER ] = ',';
    $logDelimiters[ TAB_DELIMITER ] = "\\t";
    $logDelimiters[ SPACE_DELIMITER ] = " ";
    $logDelimiters[ NO_DELIMITER ] = "";

    define( 'ARCHIVE_NONE',         "None" );
    define( 'ARCHIVE_DAILY',        "Day" );
    define( 'ARCHIVE_MONTHLY',        "Month" );
    define( 'ARCHIVE_YEARLY',        "Year" );
    $archiveFrequencies[ ARCHIVE_NONE    ] = "";
    $archiveFrequencies[ ARCHIVE_DAILY   ] = "%d";
    $archiveFrequencies[ ARCHIVE_MONTHLY ] = "%m";
    $archiveFrequencies[ ARCHIVE_YEARLY  ] = "%y";

    $allLogs = array();
    $allLogs[0] = ACCESS_LOG;
    $allLogs[1] = ADMIN_LOG;
    $allLogs[2] = AUTHENTICATION_LOG;
    $allLogs[3] = ERROR_LOG;
    $allLogs[4] = SESSION_LOG;
    $allLogs[5] = TRACE_LOG;

    define( 'AVERAGEOPTIME_PARM', "AVERAGEOPTIME" );
    define( 'CLIENT_PARM', "CLIENT" );
    define( 'CLIENTIP_PARM', "CLIENTIP" );
    define( 'DURATION_PARM', "DURATION" );
    define( 'ENDTIME_PARM', "ENDTIME" );
    define( 'ERROR_PARM', "ERROR" );
    define( 'INFO_PARM', "INFO" );
    define( 'OPID_PARM', "OPID" );
    define( 'OPSFAILED_PARM', "OPSFAILED" );
    define( 'OPSPROCESSED_PARM', "OPSPROCESSED" );
    define( 'OPSRECEIVED_PARM', "OPSRECEIVED" );
    define( 'STACKTRACE_PARM', "STACKTRACE" );
    define( 'STARTTIME_PARM', "STARTTIME" );
    define( 'USER_PARM', "USER" );

    $allLogParms = array();
    $allLogParms[0]  = AVERAGEOPTIME_PARM;
    $allLogParms[1]  = CLIENT_PARM;
    $allLogParms[2]  = CLIENTIP_PARM;
    $allLogParms[3]  = DURATION_PARM;
    $allLogParms[4]  = ENDTIME_PARM;
    $allLogParms[5]  = ERROR_PARM;
    $allLogParms[6]  = INFO_PARM;
    $allLogParms[7]  = OPID_PARM;
    $allLogParms[8]  = OPSFAILED_PARM;
    $allLogParms[9]  = OPSPROCESSED_PARM;
    $allLogParms[10] = OPSRECEIVED_PARM;
    $allLogParms[11] = STACKTRACE_PARM;
    $allLogParms[12] = STARTTIME_PARM;
    $allLogParms[13] = USER_PARM;

    $allAccessLogParms = array();
    $allAccessLogParms[0] = CLIENT_PARM;
    $allAccessLogParms[1] = CLIENTIP_PARM;
    $allAccessLogParms[2] = OPID_PARM;
    $allAccessLogParms[3] = USER_PARM;

    $allAdminLogParms = array();
    $allAdminLogParms[0] = CLIENT_PARM;
    $allAdminLogParms[1] = CLIENTIP_PARM;
    $allAdminLogParms[2] = OPID_PARM;
    $allAdminLogParms[3] = USER_PARM;

    $allAuthenticationLogParms = array();
    $allAuthenticationLogParms[0] = CLIENT_PARM;
    $allAuthenticationLogParms[1] = CLIENTIP_PARM;
    $allAuthenticationLogParms[2] = USER_PARM;

    $allErrorLogParms = array();
    $allErrorLogParms[0] = CLIENT_PARM;
    $allErrorLogParms[1] = CLIENTIP_PARM;
    $allErrorLogParms[2] = ERROR_PARM;
    $allErrorLogParms[3] = STACKTRACE_PARM;
    $allErrorLogParms[4] = USER_PARM;

    $allSessionLogParms = array();
    $allSessionLogParms[0] = CLIENT_PARM;
    $allSessionLogParms[1] = CLIENTIP_PARM;
    $allSessionLogParms[2] = USER_PARM;
    $allSessionLogParms[3] = STARTTIME_PARM;
    $allSessionLogParms[4] = ENDTIME_PARM;
    $allSessionLogParms[5] = OPSFAILED_PARM;
    $allSessionLogParms[6] = OPSRECEIVED_PARM;
    $allSessionLogParms[7] = AVERAGEOPTIME_PARM;

    $allTraceLogParms = array();
    $allTraceLogParms[0] = INFO_PARM;

    class LogPropsRecord
    {
        var $enabled;
        var $filename;
        var $allPossibleParms;
        var $parmsSelected;
        var $parmsNotSelected;

        var $propSectionStr;
        var $enabledPropStr;
        var $filenamePropStr;
        var $parmsPropStr;

         function LogPropsRecord( $propSectionStr, $enabledPropStr, $filenamePropStr, $parmsPropStr, $allPossibleParms )
        {
            $this->propSectionStr = $propSectionStr;
            $this->enabledPropStr = $enabledPropStr;
            $this->filenamePropStr = $filenamePropStr;
            $this->parmsPropStr = $parmsPropStr;

            $this->enabled = false;
            $this->filename = "";
            $this->parmsSelected = array();
            $this->parmsNotSelected = array();
            CopyArray( $allPossibleParms, $this->allPossibleParms );
        }

        function GetProps( $serverAdmin )
        {
            $props = $serverAdmin->GetConfigurationProperties( $this->propSectionStr );

            $prop = $props->GetItem( $this->enabledPropStr );
            if ( $prop->GetValue() == "1" )
                $this->enabled = true;
            else
                $this->enabled = false;

            $prop = $props->GetItem( $this->filenamePropStr );
            $this->filename = $prop->GetValue();
            CleanUpPath( $this->filename );

            $prop = $props->GetItem( $this->parmsPropStr );
            $parmsStr = $prop->GetValue();
            $this->parmsSelected = array();
            $i = 0;
            $parm = strtok( $parmsStr, "," );
            while ( $parm !== false )
            {
                $parm = trim( $parm );
                $this->parmsSelected[$i] = $parm;
                $i++;
                $parm = strtok(",");
            }
            $this->parmsNotSelected = array_diff( $this->allPossibleParms, $this->parmsSelected );
        }

        function ValidateProps()
        {
            global $errInvalidLogFilename;

            if ( $this->enabled && empty( $this->filename ) )
                throw new Exception( $errInvalidLogFilename );
        }

        function SetProps( $serverAdmin )
        {
            $props = new MgPropertyCollection();

             if ( $this->enabled )
                $propVal = "1";
            else
                $propVal = "0";
            $prop = new MgStringProperty( $this->enabledPropStr, $propVal );
            $props->Add( $prop );

            $prop = new MgStringProperty( $this->filenamePropStr, $this->filename );
            $props->Add( $prop );

            $separator = "";
            $parmStr = "";
            foreach ( $this->parmsSelected as $parm )
            {
                $parmStr = $parmStr.$separator.$parm;
                $separator = ", ";
            }
            $prop = new MgStringProperty( $this->parmsPropStr, $parmStr );
            $props->Add( $prop );

            $serverAdmin->SetConfigurationProperties( $this->propSectionStr, $props );
        }

    }

    define( 'ACTIVE_LOG_STATUS',        "Active" );
    define( 'ARCHIVE_LOG_STATUS',         "Archive" );
    class LogFileRecord
    {
        var $type;
        var $status;

        function LogFileRecord( $type, $status )
        {
            $this->type = $type;
            $this->status = $status;
        }
    }

    $allLogFiles = NULL;
    function GetAllLogFiles( $serverAdmin )
    {
        global $allLogFiles;

        if ( $allLogFiles == NULL )
        {
            $allLogFiles = array();
            $mgLogFilesPropertyCollection = $serverAdmin->EnumerateLogs();
            $numLogFiles = $mgLogFilesPropertyCollection->GetCount();
            for ( $i = 0; $i < $numLogFiles; $i+=3 )
            {
                $logFileName = "";
                $logFileType = "";
                $logFileStatus = "";
                for ( $j = 0; $j < 3; $j++ )
                {
                    $logFilePropName = $mgLogFilesPropertyCollection->GetItem( $i+$j )->GetName();
                    $logFilePropVal = $mgLogFilesPropertyCollection->GetItem( $i+$j )->GetValue();

                    if ( $logFilePropName == "LogNameProperty" )
                        $logFileName = $logFilePropVal;
                    else
                    if ( $logFilePropName == "LogTypeProperty" )
                    {
                        if ( $logFilePropVal == 'Access Log' )
                            $logFileType = ACCESS_LOG;
                        else
                        if ( $logFilePropVal == 'Admin Log' )
                            $logFileType = ADMIN_LOG;
                        else
                        if ( $logFilePropVal == 'Authentication Log' )
                            $logFileType = AUTHENTICATION_LOG;
                        else
                        if ( $logFilePropVal == 'Error Log' )
                            $logFileType = ERROR_LOG;
                        else
                        if ( $logFilePropVal == 'Session Log' )
                            $logFileType = SESSION_LOG;
                        else
                        if ( $logFilePropVal == 'Trace Log' )
                            $logFileType = TRACE_LOG;
                    }
                    else
                    if ( $logFilePropName == "LogStatusProperty" )
                        $logFileStatus = $logFilePropVal;

                    if ( !empty( $logFileName ) && !empty( $logFileType ) && !empty( $logFileStatus ) )
                        $allLogFiles[ $logFileName ] = new LogFileRecord( $logFileType, $logFileStatus );
                }
            }
        }

        return $allLogFiles;
    }

    function GetLogFiles( $serverAdmin, $targetLogType )
    {
        $allLogs = GetAllLogFiles( $serverAdmin );
        $logFiles = array();

        if ( $targetLogType == ACTIVE_LOGS )
        {
            foreach ( $allLogs as $logFileName => $logFileRec )
            {
                if ( $logFileRec->status == ACTIVE_LOG_STATUS )
                    $logFiles[] = $logFileName;
            }
        }
        else
        {
            foreach ( $allLogs as $logFileName => $logFileRec )
            {
                if ( $logFileRec->status == ARCHIVE_LOG_STATUS && $logFileRec->type == $targetLogType )
                    $logFiles[] = $logFileName;
            }
        }

        return $logFiles;
    }

    function GetActiveLogFilename( $serverAdmin, $targetLogType )
    {
        $allLogs = GetAllLogFiles( $serverAdmin );
        $filename = "";

        foreach ( $allLogs as $logFileName => $logFileRec )
        {
            if ( $logFileRec->status == ACTIVE_LOG_STATUS && $logFileRec->type == $targetLogType )
            {
                $filename = $logFileName;
                break;
            }
        }
        return $filename;
    }

    function LoadLogFileTable( &$targetTable, $firstIndex, $lastIndex, $serverAdmin, $targetLogType )
    {
        $allLogs = GetAllLogFiles( $serverAdmin );
        $logFiles = GetLogFiles( $serverAdmin, $targetLogType );

        // Load the table
        $targetTable = array();
        for ( $i = $firstIndex; $i <= $lastIndex; $i++ )
        {
            $logFileName = $logFiles[ $i ];
            $targetTable[ $logFileName ] = $allLogs[ $logFileName ];
        }
    }


    ////// Packages //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    // Package Table record type
    class PackageTableRecord
    {
        var $status;

        function PackageTableRecord( $serverAdmin, $packageName )
        {
            global $errCannotGetPackageStatus;

            $this->status = "";
            $this->size = 0;

            try
            {
                $statusInfo = $serverAdmin->GetPackageStatus( $packageName );
                $this->status = ( $statusInfo != NULL ) ? $statusInfo->GetStatusMessage() : "";
                $this->size = ( $statusInfo != NULL ) ? $statusInfo->GetPackageSize() : 0;
            }
            catch ( MgException $e )
            {
                $this->status = sprintf( $errCannotGetPackageStatus, $e->GetExceptionMessage() );
            }
        }
    }

    function GetPackagesFromDB( &$targetList )
    {
        global $site;
        global $userInfo;

        $serverAdmin = new MgServerAdmin();
        $serverAdmin->Open( $userInfo );

        // Call server to get comprehensive list of packages.

        $packages = $serverAdmin->EnumeratePackages();
        $serverAdmin->Close();

        // Load table
        $targetList = array();
        $numPackages = $packages != NULL ? $packages->GetCount() : 0 ;
        for ( $i = 0; $i < $numPackages; $i++ )
        {
            $targetList[] = $packages->GetItem( $i );
        }
    }

    $packageList = NULL;
    function GetPackageList()
    {
        global $packageList;

        if ( $packageList == NULL )
            GetPackagesFromDB( $packageList );

        return $packageList;
    }

    function GetPackageCount()
    {
        $packages = GetPackageList();
        return sizeof( $packages );
    }

    function DeletePackage( $packageName )
    {
        global $site;
        global $userInfo;
        global $packageList;

        // Validate
        $packages = GetPackageList();
        if ( empty( $packageName ) || !in_array( $packageName, $packages ) )
            return false;

        // Delete the package
        $serverAdmin = new MgServerAdmin();
        $serverAdmin->Open( $userInfo );
        $serverAdmin->DeletePackage( $packageName );
        $serverAdmin->Close();

        // Update local packageList array.
        // Note that this is done rather than calling EnumeratePackages, again, because EnumeratePackages may take some time
        // to execute.
        $oldPackageList = array();
        CopyArray( $packageList, $oldPackageList );
        $packageList = array();
        foreach ( $oldPackageList as $val )
        {
            if ( $val != $packageName )
                $packageList[] = $val;
        }

        return true;
    }

    function LoadPackageTable( &$targetTable, $firstIndex, $lastIndex )
    {
        global $site;
        global $userInfo;

        $targetTable = array();
        $packages = GetPackageList();

        $serverAdmin = new MgServerAdmin();
        $serverAdmin->Open( $userInfo );

        $iPackage = -1;
        foreach ( $packages as $val )
        {
            $iPackage++;
            if ( $iPackage < $firstIndex )
                continue;
            if ( $iPackage > $lastIndex )
                break;

            $targetTable[ $val ] = new PackageTableRecord( $serverAdmin, $val );
        }

        $serverAdmin->Close();
    }

    ////// WMS //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    define( 'WMS_NAME_ITEM',                        "Service.Name");
    define( 'WMS_TITLE_ITEM',                        "Service.Title");
    define( 'WMS_FEES_ITEM',                        "Service.Fees");
    define( 'WMS_ABSTRACT_ITEM',                    "Service.Abstract");
    define( 'WMS_ACCESS_CONSTRAINTS_ITEM',            "Service.AccessConstraints");
    define( 'WMS_KEYWORDS_ITEM',                    "Service.Keywords");
    define( 'WMS_SERVER_NAME_ITEM',                    "SERVER_NAME");
    define( 'WMS_SCRIPT_NAME_ITEM',                    "SCRIPT_NAME");
    define( 'WMS_CONTACT_NAME_ITEM',                "Service.Contact.Name");
    define( 'WMS_CONTACT_ORGANIZATION_ITEM',        "Service.Contact.Organization");
    define( 'WMS_CONTACT_POSITION_ITEM',            "Service.Contact.Position");
    define( 'WMS_CONTACT_ADDRESS_TYPE_ITEM',        "Service.Contact.Address.Type");
    define( 'WMS_CONTACT_ADDRESS_STREET_ITEM',        "Service.Contact.Address.Street");
    define( 'WMS_CONTACT_ADDRESS_CITY_ITEM',        "Service.Contact.Address.City");
    define( 'WMS_CONTACT_ADDRESS_STATE_PROV_ITEM',    "Service.Contact.Address.StateProv");
    define( 'WMS_CONTACT_ADDRESS_POST_CODE_ITEM',    "Service.Contact.Address.PostCode");
    define( 'WMS_CONTACT_ADDRESS_COUNTRY_ITEM',        "Service.Contact.Address.Country");
    define( 'WMS_CONTACT_PHONE_ITEM',                "Service.Contact.Phone");
    define( 'WMS_CONTACT_FAX_ITEM',                    "Service.Contact.Fax");
    define( 'WMS_CONTACT_EMAIL_ITEM',                "Service.Contact.Email");

    $wmsServiceMetadataItems = array(
        WMS_NAME_ITEM,
        WMS_TITLE_ITEM,
        WMS_FEES_ITEM,
        WMS_ABSTRACT_ITEM,
        WMS_ACCESS_CONSTRAINTS_ITEM,
        WMS_KEYWORDS_ITEM,
        WMS_SERVER_NAME_ITEM,
        WMS_SCRIPT_NAME_ITEM,
        WMS_CONTACT_NAME_ITEM,
        WMS_CONTACT_ORGANIZATION_ITEM,
        WMS_CONTACT_POSITION_ITEM,
        WMS_CONTACT_ADDRESS_TYPE_ITEM,
        WMS_CONTACT_ADDRESS_STREET_ITEM,
        WMS_CONTACT_ADDRESS_CITY_ITEM,
        WMS_CONTACT_ADDRESS_STATE_PROV_ITEM,
        WMS_CONTACT_ADDRESS_POST_CODE_ITEM,
        WMS_CONTACT_ADDRESS_COUNTRY_ITEM,
        WMS_CONTACT_PHONE_ITEM,
        WMS_CONTACT_FAX_ITEM,
        WMS_CONTACT_EMAIL_ITEM );

    class WMSPropsRecord
    {
        var $serviceMetadata;
        var $keywords;

        function WMSPropsRecord()
        {
            global $wmsServiceMetadataItems;

            $this->serviceMetadata = array();
            $this->keywords = array();
            foreach ( $wmsServiceMetadataItems as $item )
                $this->serviceMetadata[ $item ] = "";
        }

        function GetProps()
        {
            global $site;
            global $userInfo;
            global $errInvalidWMSFile;

            // Get WMS reader
            $serverAdmin = new MgServerAdmin();
            $serverAdmin->Open( $userInfo );
            $wmsReader = $serverAdmin->GetDocument( 'Wms:OgcWmsService.config' );
            $serverAdmin->Close();

            // Get WMS metadata
            $wmsData = "";
            $chunk = "";
            do {
                $chunkSize = $wmsReader->Read( $chunk, 4096 );
                $wmsData = $wmsData.$chunk;
            } while ( $chunkSize != 0 );
            foreach ( $this->serviceMetadata as $serviceItem => $serviceVal )
            {
                $this->serviceMetadata[ $serviceItem ] = "";
                $itemPos = strpos( $wmsData, $serviceItem );
                if ( $itemPos === false )
                    throw new Exception( $errInvalidWMSFile );
                $valStartPos = strpos( $wmsData, '>', $itemPos );
                $valEndPos = strpos( $wmsData, '</Define>', $itemPos );
                if ( $valStartPos === false || $valEndPos === false || $valStartPos >= $valEndPos )
                    throw new Exception( $errInvalidWMSFile );
                $this->serviceMetadata[ $serviceItem ] = substr( $wmsData, $valStartPos + 1, $valEndPos - $valStartPos - 1 );
            }
            $this->keywords = array();
            if ( !empty( $this->serviceMetadata[ WMS_KEYWORDS_ITEM ] ) )
            {
                $keywordsStr = $this->serviceMetadata[ WMS_KEYWORDS_ITEM ];
                $keywordStart = strpos( $keywordsStr, '<item>' );
                $keywordEnd = strpos( $keywordsStr, '</item>' );
                while ( $keywordStart !== false && $keywordEnd !== false && ($keywordStart + 6) < $keywordEnd )
                {
                    $keywordStart += 6; // Length of '<item>'
                    $this->keywords[] = substr( $keywordsStr, $keywordStart, $keywordEnd - $keywordStart );
                    $keywordEnd += 7;  // Length of '</item>'
                    $keywordStart = strpos( $keywordsStr, '<item>', $keywordEnd );
                    $keywordEnd = strpos( $keywordsStr, '</item>', $keywordEnd );
                }
            }
        }

        function ValidateProps()
        {
            $xmlReservedChars = '"\'&<>\\';
            foreach ( $this->serviceMetadata as $item => $val )
                if ( strcspn( $val, $xmlReservedChars ) != strlen( $val ) )
                    throw new Exception ( "Value ".$val." contains invalid character(s) (\"'&<>\\)." );
        }

        function SetProps()
        {
            global $site;
            global $userInfo;
            global $errInvalidWMSFile;

            // Get WMS reader
            $serverAdmin = new MgServerAdmin();
            $serverAdmin->Open( $userInfo );
            $wmsReader = $serverAdmin->GetDocument( 'Wms:OgcWmsService.config' );

            // Set WMS metadata
            $wmsData = "";
            $chunk = "";
            do {
                $chunkSize = $wmsReader->Read( $chunk, 4096 );
                $wmsData = $wmsData.$chunk;
            } while ( $chunkSize != 0 );
            $keywordsStr = "";
            foreach( $this->keywords as $keyword )
                $keywordsStr = $keywordsStr.'<item>'.$keyword.'</item>';
            $this->serviceMetadata[ WMS_KEYWORDS_ITEM ] = $keywordsStr;
            foreach ( $this->serviceMetadata as $serviceItem => $serviceVal )
            {
                $itemPos = strpos( $wmsData, $serviceItem );
                if ( $itemPos === false )
                    throw new Exception( $errInvalidWMSFile );
                $valStartPos = strpos( $wmsData, '>', $itemPos );
                $valEndPos = strpos( $wmsData, '</Define>', $itemPos );
                if ( $valStartPos === false || $valEndPos === false || $valStartPos >= $valEndPos )
                    throw new Exception( $errInvalidWMSFile );
                $wmsData = substr_replace( $wmsData, $serviceVal, $valStartPos + 1, $valEndPos - $valStartPos - 1 );
            }

            // Save wms config
            $wmsByteSource = new MgByteSource( $wmsData, strlen( $wmsData ) );
            $wmsByteSource->SetMimeType( $wmsReader->GetMimeType() );
            $serverAdmin->SetDocument( 'Wms:OgcWmsService.config', $wmsByteSource->GetReader() );
            $serverAdmin->Close();
        }
    }

   ////// WFS //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    define( 'WFS_NAME_ITEM',                        "Service.Name");
    define( 'WFS_TITLE_ITEM',                        "Service.Title");
    define( 'WFS_FEES_ITEM',                        "Service.Fees");
    define( 'WFS_ABSTRACT_ITEM',                    "Service.Abstract");
    define( 'WFS_ACCESS_CONSTRAINTS_ITEM',            "Service.AccessConstraints");
    define( 'WFS_KEYWORDS_ITEM',                    "Service.Keywords");
    define( 'WFS_SERVER_NAME_ITEM',                    "SERVER_NAME");
    define( 'WFS_SCRIPT_NAME_ITEM',                    "SCRIPT_NAME");
    define( 'WFS_CONTACT_NAME_ITEM',                "Service.Contact.Name");
    define( 'WFS_CONTACT_ORGANIZATION_ITEM',        "Service.Contact.Organization");
    define( 'WFS_CONTACT_POSITION_ITEM',            "Service.Contact.Position");
    define( 'WFS_CONTACT_ADDRESS_TYPE_ITEM',        "Service.Contact.Address.Type");
    define( 'WFS_CONTACT_ADDRESS_STREET_ITEM',        "Service.Contact.Address.Street");
    define( 'WFS_CONTACT_ADDRESS_CITY_ITEM',        "Service.Contact.Address.City");
    define( 'WFS_CONTACT_ADDRESS_STATE_PROV_ITEM',    "Service.Contact.Address.StateProv");
    define( 'WFS_CONTACT_ADDRESS_POST_CODE_ITEM',    "Service.Contact.Address.PostCode");
    define( 'WFS_CONTACT_ADDRESS_COUNTRY_ITEM',        "Service.Contact.Address.Country");
    define( 'WFS_CONTACT_PHONE_ITEM',                "Service.Contact.Phone");
    define( 'WFS_CONTACT_FAX_ITEM',                    "Service.Contact.Fax");
    define( 'WFS_CONTACT_EMAIL_ITEM',                "Service.Contact.Email");

    $wfsServiceMetadataItems = array(
        WFS_NAME_ITEM,
        WFS_TITLE_ITEM,
        WFS_FEES_ITEM,
        WFS_ABSTRACT_ITEM,
        WFS_ACCESS_CONSTRAINTS_ITEM,
        WFS_KEYWORDS_ITEM,
        WFS_SERVER_NAME_ITEM,
        WFS_SCRIPT_NAME_ITEM,
        WFS_CONTACT_NAME_ITEM,
        WFS_CONTACT_ORGANIZATION_ITEM,
        WFS_CONTACT_POSITION_ITEM,
        WFS_CONTACT_ADDRESS_TYPE_ITEM,
        WFS_CONTACT_ADDRESS_STREET_ITEM,
        WFS_CONTACT_ADDRESS_CITY_ITEM,
        WFS_CONTACT_ADDRESS_STATE_PROV_ITEM,
        WFS_CONTACT_ADDRESS_POST_CODE_ITEM,
        WFS_CONTACT_ADDRESS_COUNTRY_ITEM,
        WFS_CONTACT_PHONE_ITEM,
        WFS_CONTACT_FAX_ITEM,
        WFS_CONTACT_EMAIL_ITEM);

    class WFSPropsRecord
    {
        var $serviceMetadata;
        var $keywords;

        function WFSPropsRecord()
        {
            global $wfsServiceMetadataItems;

            $this->serviceMetadata = array();
            $this->keywords = array();
            foreach ( $wfsServiceMetadataItems as $item )
                $this->serviceMetadata[ $item ] = "";
        }

        function GetProps()
        {
            global $site;
            global $userInfo;
            global $errInvalidWFSFile;

            // Get WFS reader
            $serverAdmin = new MgServerAdmin();
            $serverAdmin->Open( $userInfo );
            $wfsReader = $serverAdmin->GetDocument( 'Wfs:OgcWfsService.config' );
            $serverAdmin->Close();

            // Get WFS metadata
            $wfsData = "";
            $chunk = "";
            do {
                $chunkSize = $wfsReader->Read( $chunk, 4096 );
                $wfsData = $wfsData.$chunk;
            } while ( $chunkSize != 0 );
            foreach ( $this->serviceMetadata as $serviceItem => $serviceVal )
            {
                $this->serviceMetadata[ $serviceItem ] = "";
                $itemPos = strpos( $wfsData, $serviceItem );
                if ( $itemPos === false )
                    throw new Exception( $errInvalidWFSFile );
                $valStartPos = strpos( $wfsData, '>', $itemPos );
                $valEndPos = strpos( $wfsData, '</Define>', $itemPos );
                if ( $valStartPos === false || $valEndPos === false || $valStartPos >= $valEndPos )
                    throw new Exception( $errInvalidWFSFile );
                $this->serviceMetadata[ $serviceItem ] = substr( $wfsData, $valStartPos + 1, $valEndPos - $valStartPos - 1 );
            }
            $this->keywords = array();
            if ( !empty( $this->serviceMetadata[ WFS_KEYWORDS_ITEM ] ) )
            {
                $keywordsStr = $this->serviceMetadata[ WFS_KEYWORDS_ITEM ];
                $keywordStart = strpos( $keywordsStr, '<item>' );
                $keywordEnd = strpos( $keywordsStr, '</item>' );
                while ( $keywordStart !== false && $keywordEnd !== false && ($keywordStart + 6) < $keywordEnd )
                {
                    $keywordStart += 6; // Length of '<item>'
                    $this->keywords[] = substr( $keywordsStr, $keywordStart, $keywordEnd - $keywordStart );
                    $keywordEnd += 7;  // Length of '</item>'
                    $keywordStart = strpos( $keywordsStr, '<item>', $keywordEnd );
                    $keywordEnd = strpos( $keywordsStr, '</item>', $keywordEnd );
                }
            }
        }

        function ValidateProps()
        {
            $xmlReservedChars = '"\'&<>\\';
            foreach ( $this->serviceMetadata as $item => $val )
                if ( strcspn( $val, $xmlReservedChars ) != strlen( $val ) )
                    throw new Exception ( "Value ".$val." contains invalid character(s) (\"'&<>\\)." );
        }

        function SetProps()
        {
            global $site;
            global $userInfo;
            global $errInvalidWFSFile;

            // Get WFS reader
            $serverAdmin = new MgServerAdmin();
            $serverAdmin->Open( $userInfo );
            $wfsReader = $serverAdmin->GetDocument( 'Wfs:OgcWfsService.config' );

            // Set WFS metadata
            $wfsData = "";
            $chunk = "";
            do {
                $chunkSize = $wfsReader->Read( $chunk, 4096 );
                $wfsData = $wfsData.$chunk;
            } while ( $chunkSize != 0 );
            $keywordsStr = "";
            foreach( $this->keywords as $keyword )
                $keywordsStr = $keywordsStr.'<item>'.$keyword.'</item>';
            $this->serviceMetadata[ WFS_KEYWORDS_ITEM ] = $keywordsStr;
            foreach ( $this->serviceMetadata as $serviceItem => $serviceVal )
            {
                $itemPos = strpos( $wfsData, $serviceItem );
                if ( $itemPos === false )
                    throw new Exception( $errInvalidWFSFile );
                $valStartPos = strpos( $wfsData, '>', $itemPos );
                $valEndPos = strpos( $wfsData, '</Define>', $itemPos );
                if ( $valStartPos === false || $valEndPos === false || $valStartPos >= $valEndPos )
                    throw new Exception( $errInvalidWFSFile );
                $wfsData = substr_replace( $wfsData, $serviceVal, $valStartPos + 1, $valEndPos - $valStartPos - 1 );
            }

            // Save wfs config
            $wfsByteSource = new MgByteSource( $wfsData, strlen( $wfsData ) );
            $wfsByteSource->SetMimeType( $wfsReader->GetMimeType() );
            $serverAdmin->SetDocument( 'Wfs:OgcWfsService.config', $wfsByteSource->GetReader() );
            $serverAdmin->Close();
        }
    }


    //represent on setting of Performance report
    class RecentSetting
    {
        public $SettingId;
        public $MapResourceId;
        public $CreateTime;
        public $ModifyTime;
        public $CenterPoint;
        public $Scale;
    }

    //A class which contains a list of recentSetting
    //and some methods to manipulate the recent settings
    class RecentSettings
    {
        public $recentSettings;

        public function GetRecentSettings()
        {
            $recentSettingsDoc = new DOMDocument();
            $recentSettingsFile = "profilingmapxml/RecentSettings.xml";

            if(file_exists($recentSettingsFile))
            {
                $recentSettingsDoc->load($recentSettingsFile);
                $settingList = $recentSettingsDoc->documentElement->getElementsByTagName("Setting");

                //only fetch latest 10 records
                for($i = 0; $i < $settingList->length && $i < 10; $i++)
                {
                    $SettingNode = $settingList->item($i);
                    $recentSetting = new RecentSetting();

                    foreach ($SettingNode->childNodes as $cNode)
                    {
                        if(1 == $cNode->nodeType)
                        {
                            switch ($cNode->nodeName)
                            {
                                case "mapResourceId":
                                    $recentSetting->MapResourceId = $cNode->nodeValue;
                                    break;
                                case "createTime":
                                    $recentSetting->CreateTime = $cNode->nodeValue;
                                    break;
                                case "modifyTime":
                                    $recentSetting->ModifyTime = $cNode->nodeValue;
                                    break;
                                case "centerPoint":
                                    $recentSetting->CenterPoint = $cNode->nodeValue;
                                    break;
                                case "scale":
                                    $recentSetting->Scale = $cNode->nodeValue;
                                    break;
                                default :break;
                            }
                        }
                    }

                    $recentSetting->SettingId = $SettingNode->getAttribute("ID");
                    $this->recentSettings[$i] = $recentSetting;
                }
            }
        }

        //when a new setting is run, save the setting to the xml file
        public function SaveRecentSettings($mapResourceId, $centerPoint, $scale)
        {
            $mapResourceId = trim($mapResourceId);
            $centerPoint = trim($centerPoint);
            $scale = trim($scale);
            
            //required parameters are null or empty then ignore this setting
            if( (!isset($mapResourceId,$centerPoint,$scale)) || ("" == trim($mapResourceId)) || ("" == trim($centerPoint)) || ("" == trim($scale)) )
            {
                return;
            }

            $recentSettingsDoc = new DOMDocument();
            $recentSettingsFile = "profilingmapxml/RecentSettings.xml";
            $rootElement;
            $settingElement;

            //get the root element <RecentSettings>
            //if the file already exists, get the documentElement
            //if not exists, create new element and append to the DOMDocument
            if(file_exists($recentSettingsFile))
            {
                $recentSettingsDoc->load($recentSettingsFile);
                $rootElement = $recentSettingsDoc->documentElement;
                
                //search the xml doc to see if the same setting is existed
                $settingElement = $this->GetExistedSetting($rootElement, $mapResourceId, $centerPoint, $scale);
                
                if(!isset ($settingElement))
                {
                    //if not existed, create the new one
                    $settingElement = $this->CreateNewSettting($recentSettingsDoc, $mapResourceId, $centerPoint, $scale);
                }
                else
                {
                    //if existed, update the modify time
                    $settingElement->getElementsByTagName("modifyTime")->item(0)->nodeValue = date("F d ,Y h:i:s A");
                }
            }
            else
            {
                $rootElement = $recentSettingsDoc->createElement("RecentSettings");
                $recentSettingsDoc->appendChild($rootElement);
                
                $settingElement = $this->CreateNewSettting($recentSettingsDoc, $mapResourceId, $centerPoint, $scale);
            }

            //add the setting node at the top
            if($rootElement->hasChildNodes())
            {
                $firstSetting = $rootElement->firstChild;
               
                //if the found one is the first one, do noting
                if($settingElement === $firstSetting)
                {
                    ;
                }
                else
                {
                    $firstSetting->parentNode->insertBefore($settingElement, $firstSetting);
                }
            }
            else
            {
                $rootElement->appendChild($settingElement);
            }

            $recentSettingsDoc = $this->DeleteSettings($recentSettingsDoc);
            //save the setting to the file
            $recentSettingsDoc->save($recentSettingsFile);
        }
        
        //find the setting that has already existed in the xml file
        public function GetExistedSetting($rootElement, $mapResourceId, $centerPoint, $scale)
        {
            $foundElement = null;
            $settingList = $rootElement->getElementsByTagName("Setting");
            
            foreach($settingList as $settingNode)
            {
                $mapIdValue = trim($settingNode->getElementsByTagName("mapResourceId")->item(0)->nodeValue);
                $centerPointValue = trim($settingNode->getElementsByTagName("centerPoint")->item(0)->nodeValue);
                $scaleValue = trim($settingNode->getElementsByTagName("scale")->item(0)->nodeValue);

                if( ($mapResourceId == $mapIdValue) && ($centerPoint == $centerPointValue) && ($scale == $scaleValue) )
                {
                    $foundElement = $settingNode;
                    break;
                }
            }
            
            return $foundElement;
        }
        
        //create new element for the setting 
        public function CreateNewSettting($recentSettingsDoc, $mapResourceId, $centerPoint, $scale)
        {
            $newSettingElement = $recentSettingsDoc->createElement("Setting");
            //use settingId to identify the unique setting
            $settingId = date("YmdHis") . rand(10, 99);
            $newSettingElement->setAttribute("ID", $settingId);

            $mapResourceIdElement = $recentSettingsDoc->createElement("mapResourceId", $mapResourceId);
            $newSettingElement->appendChild($mapResourceIdElement);

            $centerPointElement = $recentSettingsDoc->createElement("centerPoint", $centerPoint);
            $newSettingElement->appendChild($centerPointElement);

            $scaleElement = $recentSettingsDoc->createElement("scale", $scale);
            $newSettingElement->appendChild($scaleElement);

            $createDate = date("F d ,Y h:i:s A");
            $createTimeElement = $recentSettingsDoc->createElement("modifyTime", $createDate);
            $newSettingElement->appendChild($createTimeElement);

            //when create new setting, the create Time and the modify time is the same
            $modifyTimeElement = $recentSettingsDoc->createElement("createTime", $createDate);
            $newSettingElement->appendChild($modifyTimeElement);
            
            return $newSettingElement;
        }

        //To avoid the recent file to be too large, we will remove the old settings 
        //if the total records exceed 1000
        public function DeleteSettings($recentSettingsDoc)
        {
            $newXMLDoc;
            $settingElements = $recentSettingsDoc->documentElement->getElementsByTagName("Setting");
            $settingCount = $settingElements->length;
            
            if($settingCount >= 1000)
            {
                $newXMLDoc = new DOMDocument();
                $rootElement1 = $newXMLDoc->createElement("RecentSettings");
                $newXMLDoc->appendChild($rootElement1);
                
                for($i = 0; $i < 10; $i++)
                {
                    $tempNode = $this->CopyNode($newXMLDoc,$settingElements->item($i));
                    $rootElement1->appendChild($tempNode);
                }
            }
            else
            {
                $newXMLDoc = $recentSettingsDoc;
            }
            
            return $newXMLDoc;
        }

        //copy the existed nodes to the new xml document
        private function CopyNode($doc, $oldNode)
        {
            $nd = $doc->createElement("Setting");
            
            $settingIdValue = trim( $oldNode->getAttribute("ID") );
            $mapIdValue = trim( $oldNode->getElementsByTagName("mapResourceId")->item(0)->nodeValue );
            $centerPointValue = trim( $oldNode->getElementsByTagName("centerPoint")->item(0)->nodeValue );
            $scaleValue = trim( $oldNode->getElementsByTagName("scale")->item(0)->nodeValue );
            $createTimeValue = trim( $oldNode->getElementsByTagName("createTime")->item(0)->nodeValue );
            $modifyTimeValue = trim( $oldNode->getElementsByTagName("modifyTime")->item(0)->nodeValue );

            $nd->setAttribute("ID", $settingIdValue);

            $mapResourceIdElement = $doc->createElement("mapResourceId", $mapIdValue);
            $nd->appendChild($mapResourceIdElement);

            $centerPointElement = $doc->createElement("centerPoint", $centerPointValue);
            $nd->appendChild($centerPointElement);

            $scaleElement = $doc->createElement("scale", $scaleValue);
            $nd->appendChild($scaleElement);

            $createTimeElement = $doc->createElement("modifyTime", $createTimeValue);
            $nd->appendChild($createTimeElement);

            $modifyTimeElement = $doc->createElement("createTime", $modifyTimeValue);
            $nd->appendChild($modifyTimeElement);
            
            return $nd;
         }
    }

    class LayerDefinitionProfileData
    {
        public $LayerName;
        public $LayerResuorceId;
        public $TotalRenderTime;
        public $FeatureClass;
        public $CoordinateSystem;
        public $LayerType;
        public $Filters;
        public $ScaleRange;
        //if exception happened when profiling layer, the error mesage will be saved here,
        //if the $ErrorMessage is empty or null, it means no error happened
        public $ErrorMessage;

        public function GetRenderTimePercentage($sumofRenderTime)
        {
            $percentage = 0.0;

            if($sumofRenderTime > 0.0)
            {
                $percentage = $this->TotalRenderTime/$sumofRenderTime;
            }

            return number_format($percentage*100,1);
        }
    }

    class LayerDefinitionProfileResults
    {
        // an array of LayerDefinitionProfileData,
        // which consist the profiling map layers info
        public $LayerProfileDataCollection;

        public $HasErrors;

        //get the sum of each layer render time
        //note that this sum can be different with the $TotalLayerRenderTime
        public function GetSumOfLayerRenderTime()
        {
            $sum = 0.0;

            foreach ($this->LayerProfileDataCollection as $layerProfileData)
            {
                $sum = $sum + $layerProfileData->TotalRenderTime;
            }

            return $sum;
        }
    }

    class MapDefinitionProfileData
    {
        public $TotalLayerRenderTime;
        public $TotalLabelRenderTime;
        public $TotalWatermarkRenderTime;
        public $TotalImageRenderTime;
        public $TotalMapRenderTime;

        public $MapResourceId;
        public $BaseLayerCount;
        public $LayerCount;
        public $ImageFormat;
        public $CenterPoint;
        public $DataExtents;
        public $CoordinateSystem;
        public $Scale;
        public $RenderType;

        public $LabelsErrorMessage;
        public $WatermarksErrorMessage;
        public $MapErrorMessage;

        public function GetTotalRenderTime()
        {
            return $this->TotalLayerRenderTime+$this->TotalLabelRenderTime
                    +$this->TotalImageRenderTime+$this->TotalWatermarkRenderTime;
        }

        public function GetLayerRenderPercent()
        {
            return number_format($this->TotalLayerRenderTime/$this->TotalMapRenderTime*100,1);
        }

        public function GetLabelRenderPercent()
        {
            return number_format($this->TotalLabelRenderTime/$this->TotalMapRenderTime*100,1);
        }

        public function GetWartermarkRenderPercent()
        {
            return number_format($this->TotalWatermarkRenderTime/$this->TotalMapRenderTime*100,1);
        }

        public function GetImageRenderPercent()
        {
            return number_format($this->TotalImageRenderTime/$this->TotalMapRenderTime*100,1);
        }

        public function GetOtherRenderTime()
        {
            return $this->TotalMapRenderTime-$this->TotalLayerRenderTime-$this->TotalLabelRenderTime-$this->TotalWatermarkRenderTime-$this->TotalImageRenderTime;
        }

        public function GetOthersRenderPercent()
        {
            $other= $this->TotalMapRenderTime-$this->TotalLayerRenderTime-$this->TotalLabelRenderTime-$this->TotalWatermarkRenderTime-$this->TotalImageRenderTime;

            return number_format($other/$this->TotalMapRenderTime*100,1);
        }
    }

    class MapProfileResult
    {
        public $MapProfileData;
        public $LayerProfileData;

        public function ReadFromXML($resultSource)
        {
            $this->MapProfileData = new MapDefinitionProfileData();

            $this->MapProfileData->MapErrorMessage = null;
            $this->MapProfileData->LabelsErrorMessage = null;
            $this->MapProfileData->WatermarksErrorMessage = null;

            $mapResultList = $resultSource->documentElement->getElementsByTagName("ProfileRenderMapResult");
            $profileRenderMap = $mapResultList->item(0);

            //if the map has no watermark, then the returned results will not contain the node ProfileRenderWatermarksResult
            $watermarkNodeList = $profileRenderMap->getElementsByTagName("ProfileRenderWatermarksResult");
            if(0 == $watermarkNodeList->length)
            {
                $this->MapProfileData->TotalWatermarkRenderTime = 0.00;
            }

            if ($profileRenderMap->hasChildNodes()) 
            {
                foreach ($profileRenderMap->childNodes as $node)
                {
                    if (1 == $node->nodeType)
                    {
                        switch ($node->nodeName)
                        {
                            case "ResourceId":
                                $this->MapProfileData->MapResourceId = $node->nodeValue;
                                break;
                            case "CoordinateSystem":
                                $this->MapProfileData->CoordinateSystem = $node->nodeValue;
                                break;
                            case "Extents":
                                $minX = $node->getElementsByTagName("MinX")->item(0)->nodeValue;
                                $maxX = $node->getElementsByTagName("MaxX")->item(0)->nodeValue;
                                $minY = $node->getElementsByTagName("MinY")->item(0)->nodeValue;
                                $maxY = $node->getElementsByTagName("MaxY")->item(0)->nodeValue;

                                $this->MapProfileData->DataExtents = $minX.",".$minY.",".$maxX.",".$maxY;
                                break;
                            case "LayerCount":
                                $this->MapProfileData->LayerCount = $node->nodeValue;
                                break;
                            case "Scale":
                                $this->MapProfileData->Scale = $node->nodeValue;
                                break;
                            case "ImageFormat":
                                $this->MapProfileData->ImageFormat = $node->nodeValue;
                                break;
                            case "RendererType":
                                $this->MapProfileData->RenderType = $node->nodeValue;
                                break;
                            case "RenderTime":
                                $this->MapProfileData->TotalMapRenderTime = $node->nodeValue;
                                break;
                            case "CreateImageTime":
                                $this->MapProfileData->TotalImageRenderTime = $node->nodeValue;
                                break;
                            case "ProfileRenderLabelsResult":
                                foreach ($node->childNodes as $labelNode)
                                {
                                    if ($labelNode->nodeType == 1 && $labelNode->nodeName == "RenderTime")
                                    {
                                        $this->MapProfileData->TotalLabelRenderTime = $labelNode->nodeValue;
                                    }
                                    //if profiling labels has exception, save the error message here
                                    if ($labelNode->nodeType == 1 && $labelNode->nodeName == "Error")
                                    {
                                        $this->MapProfileData->LabelsErrorMessage = $labelNode->nodeValue;
                                    }
                                }
                                break;
                            case "ProfileRenderLayersResult":
                                foreach ($node->childNodes as $layerNode)
                                {
                                    if (1 == $layerNode->nodeType && "RenderTime" == $layerNode->nodeName)
                                    {
                                        $this->MapProfileData->TotalLayerRenderTime = $layerNode->nodeValue;
                                    }
                                }
                                $this->ParseLayerResult($node);
                                break;
                            case "ProfileRenderWatermarksResult":
                                foreach ($node->childNodes as $watermarkNode)
                                {
                                    if (1 == $watermarkNode->nodeType && "RenderTime" == $watermarkNode->nodeName)
                                    {
                                        $this->MapProfileData->TotalWatermarkRenderTime = $watermarkNode->nodeValue;
                                    }
                                    if (1 == $watermarkNode->nodeType && "ProfileRenderWatermarkResult" == $watermarkNode->nodeName)
                                    {
                                        foreach ($watermarkNode->childNodes as $watermarkItemNode) {
                                            if (1 == $watermarkItemNode->nodeType && "Error" == $watermarkItemNode->nodeName) {
                                                 $this->MapProfileData->WatermarksErrorMessage.= $watermarkItemNode->nodeValue . "\n";
                                            }
                                        }
                                    }
                                }
                                break;
                            case "Error":
                                $this->MapProfileData->MapErrorMessage = $node->nodeValue;
                                break;
                            default: break;
                        }
                    }
                }
            }
        }

        private function ParseLayerResult($LayerNodeList)
        {
            $this->LayerProfileData = new LayerDefinitionProfileResults();
            $this->LayerProfileData->LayerProfileDataCollection = array();
            $this->LayerProfileData->HasErrors = 0;

            foreach ($LayerNodeList->childNodes as $layerNode) {
                if (1 == $layerNode->nodeType && "ProfileRenderLayerResult" == $layerNode->nodeName) {

                    $tempLayerProfileData = new LayerDefinitionProfileData();
                    $tempLayerProfileData->ErrorMessage = null;

                    foreach ($layerNode->childNodes as $node) {
                        if (1 == $node->nodeType) {
                            switch ($node->nodeName) {
                                case "LayerName":
                                    $tempLayerProfileData->LayerName = $node->nodeValue;
                                    break;
                                case "ResourceId":
                                    $tempLayerProfileData->LayerResuorceId = $node->nodeValue;
                                    break;
                                case "LayerType":
                                    $tempLayerProfileData->LayerType = $node->nodeValue;
                                    break;
                                case "FeatureClassName":
                                    $tempLayerProfileData->FeatureClass = $node->nodeValue;
                                    break;
                                case "CoordinateSystem":
                                    $tempLayerProfileData->CoordinateSystem = $node->nodeValue;
                                    break;
                                case "RenderTime":
                                    $tempLayerProfileData->TotalRenderTime = $node->nodeValue;
                                    break;
                                case "ScaleRange":
                                    $minScale = $node->getElementsByTagName("MinScale")->item(0)->nodeValue;
                                    $maxScale = $node->getElementsByTagName("MaxScale")->item(0)->nodeValue;
                                    if($maxScale === "1000000000000")
                                    {
                                        $maxScale = "Infinity";
                                    }
                                    $tempLayerProfileData->ScaleRange= $minScale." - ".$maxScale;
                                    break;
                                case "Filter":
                                    $tempLayerProfileData->Filters= $node->nodeValue;
                                    break;
                                case "Error":
                                    $this->LayerProfileData->HasErrors++;
                                    $tempLayerProfileData->ErrorMessage = $node->nodeValue;
                                    break;
                                default:break;
                            }
                        }
                    }
                    $this->LayerProfileData->LayerProfileDataCollection[$tempLayerProfileData->LayerName] = $tempLayerProfileData;
                }
            }
        }

        //Get the base map layer count of the specified map resource
        //The base map layer count is not provided by the profiling report
        //but we need it on UI
        public function GetBaseLayerCount()
        {
            try
            {
                //site and userInfo are saved in the session
                global $site;
                global $userInfo;
                $mapResourceContent = "";
                $resourceID = $this->MapProfileData->MapResourceId;

                //connect to the site and get a resource service instance
                $siteConn = new MgSiteConnection();
                $siteConn->Open($userInfo);
                $resourceService = $siteConn->CreateService(MgServiceType::ResourceService);
                //get the map resource content from the server
                $mgResourceID = new MgResourceIdentifier($resourceID);
                $byteReader = $resourceService->GetResourceContent($mgResourceID);

                //read the content into a string
                $chunk = "";
                do
                {
                    $chunkSize = $byteReader->Read($chunk, 4096);
                    $mapResourceContent = $mapResourceContent . $chunk;
                } while ($chunkSize != 0);

                //parse the xml data use DOMDocument
                $resourceContent = new DOMDocument();
                $resourceContent->loadXML($mapResourceContent);

                //get all the elements with the element name "BaseMapLayer"
                $baseLayers = $resourceContent->documentElement->getElementsByTagName("BaseMapLayer");

                //set the base map layer count
                $this->MapProfileData->BaseLayerCount= $baseLayers->length;
            }
            catch (Exception $exc)
            {
                //Exceptions:
                //MgInvalidRepositoryTypeException
                //MgInvalidRepositoryNameException
                //MgInvalidResourcePathException
                //MgInvalidResourceNameException
                //MgInvalidResourceTypeException
                $errorMsg = $exc->getMessage();
            }
        }

    }
?>
