Function NDW_HTTP_Framework_Setup_Events(CtrlEntId, Event, Param1, Param2, Param3, Param4, Param5, Param6, Param7, Param8, Param9, Param10, Param11, Param12, Param13, Param14, Param15)
/***********************************************************************************************************************

    This program is proprietary and is not to be used by or disclosed to others, nor is it to be copied without written
    permission from SRP Computer Solutions, Inc.

    Name        :   NDW_HTTP_Framework_Setup_Events

    Description :   This function acts as a commuter module for all events related to this window.

    Notes       :   Commuter Modules are automatically called from the Promoted_Events function which is called by the
                    application-specific promoted event handler. This makes it possible to add QuickEvents that need to
                    execute Basic+ logic without having use the Form Designer to make the association, although this is
                    limited to the events which are currently promoted.

                    If the form needs to call the commuter module directly then the QuickEvent parameters should be
                    formatted like this:										

                        '@SELF','@EVENT',['@PARAM1','@PARAMx']

    Parameters  :
        CtrlEntId   [in] -- The fully qualified name of the control calling the promoted event
        Event       [in] -- The event being executed. See the Notes section regarding "PRE" events
        Param1-15   [in] -- Additional event parameter holders
        EventFlow  [out] -- Set to 1 or 0 so the calling event knows whether or not to chain forward. See comments in
                            EVENT_SETUP insert

    History (Date, Initials, Notes)
        11/07/16    dmb     Initial development.
        02/27/17    dmb     [SRPFW-125] Add support for Aborted Service.
        03/04/17    dmb     [SRPFW-154] Add support for Enable Logging flag.
        03/07/17    dmb     [SRPFW-155] Add support for Debugger Setting options.
        10/22/18    dmb     [SRPFW-253] Add support for Whitelisted IPs.
        10/29/18    dmb     [SRPFW-253] Insert empty row if edit table controls are created empty. This allows a new
                            item to be entered.
        10/31/18    dmb     [SRPFW-255] Add support for ActiveX controls and the new UI layout.
        11/01/18    dmb     [SRPFW-255] Add HTTP Basic Authentication configuration controls.
        11/01/18    dmb     [SRPFW-255] Add Info buttons and tooltips for all fields.
        11/03/18    dmb     [SRPFW-256] Add Server Enabled checkbox and associated logic.
        11/03/18    dmb     [SRPFW-256] Add TIMER event handler to dynamically update the Server Enabled checkbox.
        11/03/18    dmb     [SRPFW-256] Retrofit code to use Database_Services and Error_Services.
        11/07/18    dmb     [SRPFW-255] Add Resource Manager controls and UI functionality.
        11/18/18    dmb     [SRPFW-257] Add support for API Call Procedure option.
        11/19/18    dmb     [SRPFW-257] Add PUB_RESET_TOTAL_INVALID_PASSWORD_ATTEMPTS button.
        11/19/18    dmb     [SRPFW-257] Add support to call the UpdateWebAPIs service from the UpdateSetupInfo gosub if
                            there is a change in the resource manager.
        11/20/18    dmb     [SRPFW-257] Add COB_API_CALL_PROCEDURE.CHANGED event handler.
        11/20/18    dmb     [SRPFW-257] Replace CHB_SERVER_ENABLED control with PUB_SERVER_STATUS.
        11/20/18    dmb     [SRPFW-257] Add support to store SYSENV records keyed to the local application.
        11/21/18    dmb     Add OLE_PUB_CREATE_APIS.OnClick event handler so APIs can be created on-demand rather than
                            [SRPFW-257] when the Apply/OK buttons are clicked.
        11/22/18    dmb     [SRPFW-253] Update Info help for the Whitelisted IPs to indicate support for wildcards and
                            ranges.
        12/02/18    dmb     [SRPFW-255] Add EDL_RESOURCE_TITLE control and supporting logic.
        12/10/18    dmb     [SRPFW-257] Add support for sub-resources (i.e., resources of resources).
        06/24/19    dmb     [SRPFW-276] Replace calls to GetLocalAppKey gosub with calls to GetLocalAppKeyID service.
        06/24/19    dmb     [SRPFW-276] Add support for resource endpoints to be excluded from logging.
        06/24/19    dmb     [SRPFW-276] Display the SRP HTTP Framework version.
        06/24/19    dmb     [SRPFW-276] Update the Home URL help in the DisplayInfoHelp service.
        09/24/19    dmb     [SRPFW-278] Update UpdateSetupInfo internal method to call the ReleaseAllHashTables service
                            so changes in the resources can be retrieved immediately by other services.
        09/24/19    dmb     [SRPFW-278] Update the OLE_PUB_CREATE_APIS.OnClick event handler to support prompting the
                            user to make a copy of an inherited API.
        12/09/19    dmb     [SRPFW-296] Update all calls to Memory_Services to use a specific cache name.
        12/16/19    dmb     [SRPFW-296] Update the UpdateSetupInfo internal method to get the Item key of the
                            OLE_TRE_RESOURCES control instead of hard-coding this to APIROOT. This allows the
                            UpdateURLPath internal method to set the URL correctly.
        12/16/19    dmb     [SRPFW-296] Update the WINDOW.CREATE event handler and the SetupOLEControls internal method
                            so the OLE_TRE_RESOURCES control no longer defaults all items to be expanded but the APIROOT
                            will be expanded upon launch. This is to help make it easier to see long lists of resources.
        12/16/19    dmb     [SRPFW-296] Add OLE_PUB_CONTRACT.OnClick handler to display only top-level resources.
        12/16/19    dmb     [SRPFW-296] Enable drag-and-drop with the OLE_TRE_RESOURCES control and add the
                            OLE_TRE_RESOURCES.OnDrop event so users can re-sort top-level resource items.
        01/23/20    dmb     [SRPFW-296] Modify the OLE_TRE_RESOURCE_METHODS control to display "GET / HEAD" rather than
                            "GET".
        01/23/20    dmb     [SRPFW-296] Modify the OLE_TRE_RESOURCES.OnSelChange API to support the change to
                            "GET / HEAD" in the OLE_TRE_RESOURCE_METHODS control.
        01/23/20    dmb     [SRPFW-296] Modify the OLE_TRE_RESOURCE_METHODS.OnItemCheck API to support the change to
                            "GET / HEAD" in the OLE_TRE_RESOURCE_METHODS control.
        02/12/20    dmb     [SRPFW-311] Fix minor typo in DisplayInfoHelp gosub.
        06/15/20    dmb     [SRPFW-313] Replace EDT_NON_AUTHENTICATED_URLS (an OI edit table control) with
                            OLE_EDT_NON_AUTHENTICATED_URLS (an SRP EditTable control) in preparation of adding new
                            features.
        06/15/20    dmb     [SRPFW-313] Replace EDT_WHITELISTED_IPS (an OI edit table control) with
                            OLE_EDT_WHITELISTED_IPS (an SRP EditTable control) to remain consistent with the
                            OLE_EDT_NON_AUTHENTICATED_URLS control.
        06/15/20    dmb     [SRPFW-313] Replace all references to OLE_INFO_NON_AUTHENTICATED_URLS with
                            OLE_INFO_NON_AUTHENTICATED_PATHS, including relevant labels and help verbiage. This is to
                            better describe the purpose of this feature.
        06/15/20    dmb     [SRPFW-313] Make changes where needed to support the new Query Params column in the
                            OLE_INFO_NON_AUTHENTICATED_PATHS control.
        06/30/20    dmb     [SRPFW-313] Make changes where needed to support Banned IPs.
        07/26/20    dmb     [SRPFW-313] Add support for the RAG_WHITELISTED_IPS control so that Whitelisted IPs can now
                            be configured to work as an "only IPs allowed" or "always IPs allowed" setting.
        07/26/20    dmb     [SRPFW-313] Update the DisplayInfoHelp gosub so the OLE_INFO_WHITELISTED_IPS help includes
                            information related to the whitelist type.
        12/22/21    dmb     Update the PUB_CANCEL.CLICK event handler to make the form invisible before calling
                            End_Dialog.

***********************************************************************************************************************/

#pragma precomp SRP_PreCompiler
#window NDW_HTTP_FRAMEWORK_SETUP

$insert LOGICAL
$insert MSG_EQUATES
$insert HTTP_FRAMEWORK_SETUP_EQUATES
$insert REPOSERRORS

Equ CacheName$  to 'SRPHTTPFramework'

Declare subroutine  HTTP_Services, Database_Services, Error_Services, Memory_Services, Set_Property, PlaceDialog
Declare subroutine  End_Dialog, Msg, Send_Event, Send_Message, Yield
Declare function    HTTP_Services, Database_Services, Error_Services, Get_Property, Utility, SRP_String, SRP_Get_Window_Rect
Declare function    SRP_Array

// Get the design time name of the window in case this is a multi-instance window.
Window = @Window[1, 'F*']

// Always get the CtrlClassID since we are not passing it through the event parameters.
CtrlClassId = Get_Property(CtrlEntId, 'TYPE')

// Get the name of the control on the window based on the CtrlClassId.
Begin Case
    Case CtrlClassId EQ 'WINDOW'
        Control = Window
    Case CtrlClassId EQ 'RADIOBUTTON'
        Control = Field(CtrlEntId, '.', 2, 2)
    Case CtrlClassId EQ 'MENU'
        Control = CtrlEntId[-1, 'B.']
    Case 1
        Control = Field(CtrlEntId, '.', 2, 1)
End Case

If Event EQ 'OLE' then GoSub TransferParams
GoToEvent Event for CtrlEntID
If Event EQ 'OLE' then GoSub RestoreParams

Return EventFlow OR EVENT_CONTINUE$


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Events
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

Event WINDOW.CREATE(CreateParam)

    Set_Property('SYSTEM', 'BLOCK_EVENTS', True$)

    LocalSetupKeyID = HTTP_Services('GetLocalAppKeyID', SetupRowKeyID$)
    SetupInfo       = Database_Services('ReadDataRow', SetupTable$, LocalSetupKeyID)

    GoSub SetupOLEControls

    // Create closed tab loops to avoid accidental page changes.
    Set_Property(@Window : '.OLE_TRE_MENU', 'NEXT', @Window : '.OLE_TRE_MENU')
    Set_Property(@Window : '.OLE_INFO_SERVER_ENABLED', 'NEXT', @Window : '.EDL_HOME_URL')
    Set_Property(@Window : '.OLE_EDT_BANNED_IPS', 'NEXT', @Window : '.CHB_ENABLE_AUTHENTICATION')
    Set_Property(@Window : '.OLE_PUB_RESOURCE_QUERY_PARAMS_REMOVE', 'NEXT', @Window : '.OLE_TRE_RESOURCES')

    Set_Property(@Window : '.EDL_HOME_URL', 'TEXT', SetupInfo<HTTP_FRAMEWORK_SETUP_HOME_URL$>)
    Set_Property(@Window : '.EDL_API_URL', 'TEXT', SetupInfo<HTTP_FRAMEWORK_SETUP_API_URL$>)
    APICallProcedure    = SetupInfo<HTTP_FRAMEWORK_SETUP_API_CALL_PROCEDURE$>
    If APICallProcedure EQ '' then APICallProcedure = 'Web API'
    Set_Property(@Window : '.COB_API_CALL_PROCEDURE', 'TEXT', APICallProcedure)
    Set_Property(@Window : '.CHB_ENABLE_LOGGING', 'CHECK', SetupInfo<HTTP_FRAMEWORK_SETUP_ENABLE_LOGGING$>)
    Set_Property(@Window : '.CHB_LOG_ERRORS_ONLY', 'CHECK', SetupInfo<HTTP_FRAMEWORK_SETUP_LOG_ERRORS_ONLY$>)
    Set_Property(@Window : '.EDL_CAPTURE_PATH', 'TEXT', SetupInfo<HTTP_FRAMEWORK_SETUP_CAPTURE_PATH$>)
    Set_Property(@Window : '.EDL_REALM_VALUE', 'TEXT', SetupInfo<HTTP_FRAMEWORK_SETUP_REALM_VALUE$>)
    Set_Property(@Window : '.EDL_ENTRY_POINT_SERVICE', 'TEXT', SetupInfo<HTTP_FRAMEWORK_SETUP_ENTRY_POINT_SERVICE$>)
    Set_Property(@Window : '.EDL_ABORTED_SERVICE', 'TEXT', SetupInfo<HTTP_FRAMEWORK_SETUP_ABORTED_SERVICE$>)
    DebuggerSetting     = SetupInfo<HTTP_FRAMEWORK_SETUP_DEBUGGER_SETTING$>
    If DebuggerSetting EQ '' then DebuggerSetting = 0
    DebuggerSetting    += 1
    SettingText         = Field('Disabled,Enabled,Intercept', ',', DebuggerSetting, 1)
    Set_Property(@Window : '.COB_DEBUGGER_SETTING', 'TEXT', SettingText)
    Set_Property(@Window : '.EDL_DEBUGGER_INTERCEPT', 'TEXT', SetupInfo<HTTP_FRAMEWORK_SETUP_DEBUGGER_INTERCEPT$>)
    Set_Property(@Window : '.CHB_FLUSH_CACHE', 'CHECK', SetupInfo<HTTP_FRAMEWORK_SETUP_FLUSH_CACHE$>)
    Set_Property(@Window : '.CHB_ENABLE_AUTHENTICATION', 'CHECK', SetupInfo<HTTP_FRAMEWORK_SETUP_ENABLE_AUTHENTICATION$>)
    Set_Property(@Window : '.CHB_ENABLE_HTTP_BASIC_AUTHENTICATION', 'CHECK', SetupInfo<HTTP_FRAMEWORK_SETUP_ENABLE_HTTP_BASIC_AUTHENTICATION$>)
    Set_Property(@Window : '.EDL_NEW_PASSWORD_TIME_TO_LIVE', 'TEXT', SetupInfo<HTTP_FRAMEWORK_SETUP_NEW_PASSWORD_TIME_TO_LIVE$>)
    Set_Property(@Window : '.EDL_OLD_PASSWORD_TIME_TO_LIVE', 'TEXT', SetupInfo<HTTP_FRAMEWORK_SETUP_OLD_PASSWORD_TIME_TO_LIVE$>)
    Set_Property(@Window : '.EDL_INVALID_PASSWORD_LIMIT', 'TEXT', SetupInfo<HTTP_FRAMEWORK_SETUP_INVALID_PASSWORD_LIMIT$>)
    Set_Property(@Window : '.COB_CONTAINMENT_ACTION', 'TEXT', SetupInfo<HTTP_FRAMEWORK_SETUP_CONTAINMENT_ACTION$>)
    WhitelistedIPsType  = SetupInfo<HTTP_FRAMEWORK_SETUP_WHITELISTED_IPS_TYPE$>
    If WhitelistedIPsType EQ '' then WhitelistedIPsType = 1
    Set_Property(@Window : '.RAG_WHITELISTED_IPS', 'VALUE', WhitelistedIPsType)

    Send_Event(@Window : '.COB_API_CALL_PROCEDURE', 'CHANGED')
    Send_Event(@Window : '.COB_DEBUGGER_SETTING', 'CHANGED')
    Send_Event(@Window : '.CHB_ENABLE_LOGGING', 'CLICK')
    Send_Event(@Window : '.CHB_ENABLE_AUTHENTICATION', 'CLICK')

    Attempts    = HTTP_Services('GetTotalInvalidPasswordAttempts')
    Set_Property(@Window : '.EDL_TOTAL_INVALID_PASSWORD_ATTEMPTS', 'INVALUE', Attempts)

    GoSub CheckServerStatus

    LocalSetupKeyID = HTTP_Services('GetLocalAppKeyID', ResourcesKeyID$)
    ItemList        = Database_Services('ReadDataRow', SetupTable$, LocalSetupKeyID)
    If ItemList EQ '' then
        ItemList            = ''
        ItemList<0, 1>      = 1
        ItemList<0, 2>      = 'APIROOT'
        APIRootURL          = HTTP_Services('GetAPIRootURL', False$)
        If APIRootURL[1, 1] EQ '/' then APIRootURL = APIRootURL[2, 99]
        ItemList<0, 3>      = APIRootURL
        ItemList<0, 5>      = 'APIROOT'
        ItemList<0, 19, 1>  = 'METHODS' : @TM : 'GET'
        ItemList<0, 19, 2>  = 'QUERY_PARAMS' : @TM : ''
        ItemList<0, 19, 3>  = 'DESCRIPTION' : @TM : 'This is the API entry point.'
        ItemList<0, 19, 4>  = 'TITLE' : @TM : 'API Entry Point.'
        ItemList<0, 19, 5>  = 'EXCLUDE_LOGGING' : @TM : False$
    end
    Set_Property(@Window : '.OLE_TRE_RESOURCES', 'OLE.ItemList', ItemList)
    Set_Property(@Window : '.OLE_TRE_RESOURCES', 'OLE.SelectedItems', 'APIROOT')
    Set_Property(@Window : '.OLE_TRE_RESOURCES', 'OLE.FocusItem', 'APIROOT')
    Set_Property(@Window : '.OLE_TRE_RESOURCES', 'OLE.ItemExpanded[APIROOT]', True$)
    ItemKey = 'APIROOT'
    GoSub UpdateURLPath
    Send_Event(@Window : '.OLE_TRE_RESOURCES', 'OLE', 'OnSelChange', ItemKey : '')

    Version     = Get_Property(@Window : '.STA_SRP_HTTP_FRAMEWORK_VERSION', 'TEXT')
    Swap '#' with HTTP_Services('GetVersion')[1, \0D\] in Version
    Set_Property(@Window : '.STA_SRP_HTTP_FRAMEWORK_VERSION', 'TEXT', Version)

    // Start a TIMER event to track activity and facilitate the closing of the form if needed.
    Set_Property(@Window, 'TIMER', 5000)

    PlaceDialog(-2, -2)

    Set_Property('SYSTEM', 'BLOCK_EVENTS', False$)

end event


Event WINDOW.TIMER()

    GoSub CheckServerStatus
    Attempts    = HTTP_Services('GetTotalInvalidPasswordAttempts')
    Set_Property(@Window : '.EDL_TOTAL_INVALID_PASSWORD_ATTEMPTS', 'INVALUE', Attempts)
    Yield(); Yield(); Yield(); Yield(); Yield(); Yield(); Yield(); Yield()

end event


Event EDL_HOME_URL.LOSTFOCUS(Flag, FocusID)

    GotFocusValue   = Get_Property(CtrlEntId, 'GOTFOCUS_VALUE')
    Text            = Get_Property(CtrlEntId, 'TEXT')
    If GotFocusValue NE Text then
        ItemKey = 'APIROOT'
        Send_Event(@Window : '.OLE_TRE_RESOURCES', 'OLE', 'RenameItemChildren', ItemKey : '')
        GoSub UpdateURLPath
    end

end event


Event EDL_API_URL.LOSTFOCUS(Flag, FocusID)

    GotFocusValue   = Get_Property(CtrlEntId, 'GOTFOCUS_VALUE')
    Text            = Get_Property(CtrlEntId, 'TEXT')
    If GotFocusValue NE Text then
        ItemKey = 'APIROOT'
        If Text[1, 1] NE '/' then
            Text    = '/' : Text
            Set_Property(CtrlEntId, 'TEXT', Text)
        end
        Text    = Text[2, 99]
        Set_Property(@Window : '.OLE_TRE_RESOURCES', 'OLE.ItemData[' : ItemKey : ']', Text)
        Send_Event(@Window : '.OLE_TRE_RESOURCES', 'OLE', 'RenameItemChildren', ItemKey : '')
        GoSub UpdateURLPath
    end

end event


Event PUB_OK.CLICK()

    GoSub UpdateSetupInfo
    If Error_Services('NoError') then
        Set_Property(@Window, 'VISIBLE', False$)
        End_Dialog(@Window)
    end

    EventFlow   = False$

end event


Event PUB_SERVER_STATUS.CLICK()

    Text    = Get_Property(CtrlEntId, 'TEXT')
    If Index(Text, 'Disable', 1) then
        ServerEnabled   = False$
    end else
        ServerEnabled   = True$
    end
    HTTP_Services('SetServerEnabled', ServerEnabled)
    If Error_Services('HasError') then
        MsgStruct               = ''
        MsgStruct<MTEXT$>       = Error_Services('GetMessage')
        MsgStruct<MTYPE$>       = 'BO'
        MsgStruct<MICON$>       = 'H'
        MsgStruct<MCAPTION$>    = 'SRP HTTP Framework'
        Msg(@Window, MsgStruct)
    end

end event


Event PUB_RESET_TOTAL_INVALID_PASSWORD_ATTEMPTS.CLICK()

    HTTP_Services('SetTotalInvalidPasswordAttempts', 0)

    If Error_Services('HasError') then
        MsgStruct               = ''
        MsgStruct<MTEXT$>       = Error_Services('GetMessage')
        MsgStruct<MTYPE$>       = 'BO'
        MsgStruct<MICON$>       = 'H'
        MsgStruct<MCAPTION$>    = 'SRP HTTP Framework'
        Msg(@Window, MsgStruct)
    end else
        Set_Property(@Window : '.EDL_TOTAL_INVALID_PASSWORD_ATTEMPTS', 'TEXT', 0)
    end

end event


Event PUB_CANCEL.CLICK()

    Set_Property(@Window, 'VISIBLE', False$)
    End_Dialog(@Window)
    EventFlow   = False$

end event


Event PUB_APPLY.CLICK()

    GoSub UpdateSetupInfo
    
end event


Event COB_API_CALL_PROCEDURE.CHANGED(NewData)

    CallProc    = Get_Property(CtrlEntId, 'TEXT')
    If CallProc _EQC 'HTTP Service' then
        Set_Property(@Window : '.EDL_ENTRY_POINT_SERVICE', 'ENABLED', True$)
    end else
        Set_Property(@Window : '.EDL_ENTRY_POINT_SERVICE', 'ENABLED', False$)
    end

end event


Event COB_DEBUGGER_SETTING.CHANGED(NewData)

    SettingText = Get_Property(CtrlEntId, 'TEXT')
    If SettingText _EQC 'Intercept' then
        Set_Property(@Window : '.EDL_DEBUGGER_INTERCEPT', 'ENABLED', True$)
    end else
        Set_Property(@Window : '.EDL_DEBUGGER_INTERCEPT', 'ENABLED', False$)
    end

end event


Event CHB_ENABLE_LOGGING.CLICK()

    Checked = Get_Property(CtrlEntId, 'CHECK')
    If Checked then
        Set_Property(@Window : '.STA_CAPTURE_PATH', 'ENABLED', True$)
        Set_Property(@Window : '.EDL_CAPTURE_PATH', 'ENABLED', True$)
        Set_Property(@Window : '.CHB_LOG_ERRORS_ONLY', 'ENABLED', True$)
    end else
        Set_Property(@Window : '.STA_CAPTURE_PATH', 'ENABLED', False$)
        Set_Property(@Window : '.EDL_CAPTURE_PATH', 'ENABLED', False$)
        Set_Property(@Window : '.CHB_LOG_ERRORS_ONLY', 'ENABLED', False$)
    end

end event


Event OLE_TRE_MENU.OnSelChange(Selection)

    Begin Case
        Case Selection _EQC 'Settings'
            Page        = 1
            FocusCtrl   = @Window : '.EDL_HOME_URL'
        Case Selection _EQC 'Authentication'
            Page        = 2
            FocusCtrl   = @Window : '.CHB_ENABLE_AUTHENTICATION'
        Case Selection _EQC 'Resources'
            Page        = 3
            FocusCtrl   = @Window : '.OLE_TRE_RESOURCES'
        Case Otherwise$
            Page        = 1
            FocusCtrl   = @Window : '.EDL_HOME_URL'
    End Case

    Send_Event(@Window, 'PAGE', Page)
    Send_Event(@Window, 'VSCROLL', Page)
    Set_Property('SYSTEM', 'FOCUS', FocusCtrl)
    ProgID = Get_Property(FocusCtrl, 'OLE.ProgID')
    If ProgID _EQC 'SRP.Tree.1' then
        Set_Property(FocusCtrl, 'OLE.Focus', True$)
        FocusItem   = Get_Property(FocusCtrl, 'OLE.FocusItem')
        Set_Property(FocusCtrl, 'OLE.FocusItem', FocusItem)
    end

end event


Event OLE_INFO_HOME_URL.OnMouseEnter()
Event OLE_INFO_API_URL.OnMouseEnter()
Event OLE_INFO_API_CALL_PROCEDURE.OnMouseEnter()
Event OLE_INFO_ENABLE_LOGGING.OnMouseEnter()
Event OLE_INFO_LOG_ERRORS_ONLY.OnMouseEnter()
Event OLE_INFO_CAPTURE_PATH.OnMouseEnter()
Event OLE_INFO_REALM_VALUE.OnMouseEnter()
Event OLE_INFO_ENTRY_POINT_SERVICE.OnMouseEnter()
Event OLE_INFO_ABORTED_SERVICE.OnMouseEnter()
Event OLE_INFO_DEBUGGER_SETTING.OnMouseEnter()
Event OLE_INFO_FLUSH_CACHE.OnMouseEnter()
Event OLE_INFO_SERVER_ENABLED.OnMouseEnter()
Event OLE_INFO_ENABLE_AUTHENTICATION.OnMouseEnter()
Event OLE_INFO_ENABLE_HTTP_BASIC_AUTHENTICATION.OnMouseEnter()
Event OLE_INFO_NEW_PASSWORD_TIME_TO_LIVE.OnMouseEnter()
Event OLE_INFO_OLD_PASSWORD_TIME_TO_LIVE.OnMouseEnter()
Event OLE_INFO_INVALID_PASSWORD_LIMIT.OnMouseEnter()
Event OLE_INFO_TOTAL_INVALID_PASSWORD_ATTEMPTS.OnMouseEnter()
Event OLE_INFO_CONTAINMENT_ACTION.OnMouseEnter()
Event OLE_INFO_NON_AUTHENTICATED_PATHS.OnMouseEnter()
Event OLE_INFO_WHITELISTED_IPS.OnMouseEnter()
Event OLE_INFO_BANNED_IPS.OnMouseEnter()
Event OLE_INFO_RESOURCES.OnMouseEnter()
Event OLE_INFO_RESOURCE_TITLE.OnMouseEnter()
Event OLE_INFO_RESOURCE_DESCRIPTION.OnMouseEnter()
Event OLE_INFO_RESOURCE_METHODS.OnMouseEnter()
Event OLE_INFO_RESOURCE_QUERY_PARAMS.OnMouseEnter()
Event OLE_INFO_RESOURCE_EXCLUDE_FROM_LOGGING.OnMouseEnter()

    GoSub DisplayInfoHelp

end event


Event OLE_INFO_HOME_URL.OnMouseExit()
Event OLE_INFO_API_URL.OnMouseExit()
Event OLE_INFO_API_CALL_PROCEDURE.OnMouseExit()
Event OLE_INFO_ENABLE_LOGGING.OnMouseExit()
Event OLE_INFO_LOG_ERRORS_ONLY.OnMouseExit()
Event OLE_INFO_CAPTURE_PATH.OnMouseExit()
Event OLE_INFO_REALM_VALUE.OnMouseExit()
Event OLE_INFO_ENTRY_POINT_SERVICE.OnMouseExit()
Event OLE_INFO_ABORTED_SERVICE.OnMouseExit()
Event OLE_INFO_DEBUGGER_SETTING.OnMouseExit()
Event OLE_INFO_FLUSH_CACHE.OnMouseExit()
Event OLE_INFO_SERVER_ENABLED.OnMouseExit()
Event OLE_INFO_ENABLE_AUTHENTICATION.OnMouseExit()
Event OLE_INFO_ENABLE_HTTP_BASIC_AUTHENTICATION.OnMouseExit()
Event OLE_INFO_NEW_PASSWORD_TIME_TO_LIVE.OnMouseExit()
Event OLE_INFO_OLD_PASSWORD_TIME_TO_LIVE.OnMouseExit()
Event OLE_INFO_INVALID_PASSWORD_LIMIT.OnMouseExit()
Event OLE_INFO_TOTAL_INVALID_PASSWORD_ATTEMPTS.OnMouseExit()
Event OLE_INFO_CONTAINMENT_ACTION.OnMouseExit()
Event OLE_INFO_NON_AUTHENTICATED_PATHS.OnMouseExit()
Event OLE_INFO_WHITELISTED_IPS.OnMouseExit()
Event OLE_INFO_BANNED_IPS.OnMouseExit()
Event OLE_INFO_RESOURCES.OnMouseExit()
Event OLE_INFO_RESOURCE_TITLE.OnMouseExit()
Event OLE_INFO_RESOURCE_DESCRIPTION.OnMouseExit()
Event OLE_INFO_RESOURCE_METHODS.OnMouseExit()
Event OLE_INFO_RESOURCE_QUERY_PARAMS.OnMouseExit()
Event OLE_INFO_RESOURCE_EXCLUDE_FROM_LOGGING.OnMouseExit()

    GoSub HideInfoHelp

end event


Event OLE_TRE_RESOURCES.OnSelChange(Selection)

    // Build a full endpoint URL based on the currently selected item in the URL Tree and display it in the URL Path
    // control.
    ItemKey     = Selection
    Set_Property(CtrlEntId, '@PREV_ITEMKEY', ItemKey)   ; // Used by LOSTFOCUS event handlers that might be triggered when the user changes the item selected.
    Item        = Get_Property(CtrlEntId, 'OLE.Item[' : ItemKey : ']')
    ItemClass   = Get_Property(CtrlEntId, 'OLE.ItemClass[' : ItemKey : ']')
    Methods     = Get_Property(CtrlEntId, 'OLE.ItemField[' : ItemKey : ';METHODS]')
    Swap 'GET' with 'GET / HEAD' in Methods
    MethodList  = Get_Property(@Window : '.OLE_TRE_RESOURCE_METHODS', 'OLE.ItemList')
    MethodList  = SRP_Array('Rotate', MethodList)
    MethodList  = MethodList<2>
    Convert @VM to @FM in MethodList
    For Each Method in MethodList using @FM
        Locate Method in Methods using ',' setting cPos then
            Checked = True$
        end else
            Checked = False$
        end
        Set_Property(@Window : '.OLE_TRE_RESOURCE_METHODS', 'OLE.ItemChecked[' : Method : ']', Checked)
    Next Method
    QueryParams     = Get_Property(CtrlEntId, 'OLE.ItemField[' : ItemKey : ';QUERY_PARAMS]')
    QueryParamsList = ''
    If QueryParams NE '' then
        For Each QueryParam in QueryParams using ','
            QueryParamsList<-1> = 1 : @VM : QueryParam : @VM : QueryParam : @VM : @VM : 'QUERY_PARAM'
        Next QueryParam
        Set_Property(@Window : '.OLE_TRE_RESOURCE_QUERY_PARAMS', 'OLE.ItemList', QueryParamsList)
    end else
        Send_Message(@Window : '.OLE_TRE_RESOURCE_QUERY_PARAMS', 'OLE.RemoveAllItems')
    end
    Title       = Get_Property(CtrlEntId, 'OLE.ItemField[' : ItemKey : ';TITLE]')
    Set_Property(@Window : '.EDL_RESOURCE_TITLE', 'INVALUE', Title)
    Description = Get_Property(CtrlEntId, 'OLE.ItemField[' : ItemKey : ';DESCRIPTION]')
    Set_Property(@Window : '.EDB_RESOURCE_DESCRIPTION', 'INVALUE', Description)
    ExcludeLogging  = Get_Property(CtrlEntId, 'OLE.ItemField[' : ItemKey : ';EXCLUDE_LOGGING]')
    Set_Property(@Window : '.CHB_RESOURCE_EXCLUDE_FROM_LOGGING', 'INVALUE', ExcludeLogging)
    GoSub UpdateURLPath
    Begin Case
        Case ItemClass EQ 'APIROOT'
            ResourceEnabled     = True$
            ResourceIDEnabled   = False$
            PropertyEnabled     = False$
            RemoveEnabled       = False$
        Case ItemClass EQ 'RESOURCE'
            ResourceEnabled     = True$
            ResourceIDEnabled   = True$
            PropertyEnabled     = True$
            RemoveEnabled       = True$
        Case ItemClass EQ 'RESOURCE_ID'
            ResourceEnabled     = True$
            ResourceIDEnabled   = False$
            PropertyEnabled     = True$
            RemoveEnabled       = True$
        Case ItemClass EQ 'PROPERTY'
            ResourceEnabled     = False$
            ResourceIDEnabled   = False$
            PropertyEnabled     = False$
            RemoveEnabled       = True$
    End Case
    Set_Property(@Window : '.OLE_PUB_NEW_RESOURCE', 'ENABLED', ResourceEnabled)
    Set_Property(@Window : '.OLE_PUB_NEW_RESOURCE_ID', 'ENABLED', ResourceIDEnabled)
    Set_Property(@Window : '.OLE_PUB_NEW_PROPERTY', 'ENABLED', PropertyEnabled)
    Set_Property(@Window : '.OLE_PUB_REMOVE_ENDPOINT', 'ENABLED', RemoveEnabled)

end event


Event OLE_TRE_RESOURCES.OnClick(ItemKey, Point, Button, Shift, Ctrl)

    If ItemKey NE '' AND Button _EQC 'Right' then
        ItemClass   = Get_Property(CtrlEntId, 'OLE.ItemClass[' : ItemKey : ']')
        ItemData    = Get_Property(CtrlEntId, 'OLE.ItemData[' : ItemKey : ']')
        Menu        = ''
        Begin Case
            Case ItemClass EQ 'APIROOT'
                Menu<-1>    = 'NEW_RESOURCE' : @VM : 'New Resource' : @VM : 1
                Menu<-1>    = ''
                Menu<-1>    = 'RENAME' : @VM : 'Rename "' : ItemData : '"' : @VM : 1
            Case ItemClass EQ 'RESOURCE'
                Menu<-1>    = 'NEW_RESOURCE' : @VM : 'New Resource' : @VM : 1
                Menu<-1>    = 'NEW_RESOURCE_ID' : @VM : 'New Resource ID' : @VM : 1
                Menu<-1>    = 'NEW_PROPERTY' : @VM : 'New Property' : @VM : 1
                Menu<-1>    = ''
                Menu<-1>    = 'RENAME' : @VM : 'Rename "' : ItemData : '"' : @VM : 1
                Menu<-1>    = ''
                Menu<-1>    = 'REMOVE' : @VM : 'Remove "' : ItemData : '"...' : @VM : 1
            Case ItemClass EQ 'RESOURCE_ID'
                Menu<-1>    = 'NEW_RESOURCE' : @VM : 'New Resource' : @VM : 1
                Menu<-1>    = 'NEW_PROPERTY' : @VM : 'New Property' : @VM : 1
                Menu<-1>    = ''
                Menu<-1>    = 'RENAME' : @VM : 'Rename "' : ItemData : '"' : @VM : 1
                Menu<-1>    = ''
                Menu<-1>    = 'REMOVE' : @VM : 'Remove "' : ItemData : '"...' : @VM : 1
            Case ItemClass EQ 'PROPERTY'
                Menu<-1>    = 'RENAME' : @VM : 'Rename "' : ItemData : '"' : @VM : 1
                Menu<-1>    = ''
                Menu<-1>    = 'REMOVE' : @VM : 'Remove "' : ItemData : '"...' : @VM : 1
        End Case
        Send_Message(CtrlEntId, 'OLE.ShowContextMenu', Point, Menu, ItemKey)
    end

end event


Event OLE_TRE_RESOURCES.OnDblClick(ItemKey, Point, Button, Shift, Ctrl)

    If (ItemKey NE '') AND (Button EQ 'Left') then
        Send_Message(CtrlEntId, 'OLE.EditItem', ItemKey, False$, False$, True$)
    end

end event


Event OLE_TRE_RESOURCES.AfterUpdate(ItemKey, NewText, OldText)

    // When the user renames a resource item, this needs to update the ItemKey in the Tree control. This means creating
    // a new item based on the new resource name and then removing the original item.
    Item                = Get_Property(CtrlEntId, 'OLE.Item[' : ItemKey : ']')
    ItemClass           = Get_Property(CtrlEntId, 'OLE.ItemClass[' : ItemKey : ']')
    ItemParent          = Get_Property(CtrlEntId, 'OLE.ItemParent[' : ItemKey : ']')
    ItemParentIsRoot    = Get_Property(CtrlEntID, 'OLE.ItemParentIsRoot[' : ItemKey : ']')
    OrigItemKey         = ItemKey

    If OrigItemKey EQ 'NEWITEM' then
        Locate NewText in '[Resource]' : @FM : '{Resource ID}' : @FM : '<Property>' using @FM setting Pos then
            // User did not change the default new resource name so remove it.
            Send_Message(CtrlEntId, 'OLE.RemoveItems', OrigItemKey)
            // Make the parent item the current item so it can get focus.
            ItemKey = ItemParent
        end else
            // Get a fully qualified URL path for the current value of ItemKey.
            ItemKey     = ItemParent
            GoSub GetURLPath
            If ItemClass EQ 'RESOURCE_ID' then
                // Make sure the resource ID is surrounded by curly braces.
                If NewText[1, 1] NE '{' then NewText = '{' : NewText
                If NewText[-1, 1] NE '}' then NewText := '}'
                Set_Property(CtrlEntId, 'OLE.Item[' : OrigItemKey : ']', NewText)
            end
            ItemKey     = URLPath : '/' : NewText
            Convert '.' to '_' in ItemKey
            Item    = Get_Property(CtrlEntId, 'OLE.Item[' : ItemKey : ']')
            If Item EQ '' then
                // Rename the original ItemKey to the new ItemKey.
                Send_Message(CtrlEntId, 'OLE.ChangeKeys', OrigItemKey, ItemKey)
                Send_Event(CtrlEntId, 'OLE', 'RenameItemChildren', ItemKey : '')
            end else
                // User created an item that already exists. Put this back in edit mode.
                MsgStruct               = ''
                MsgStruct<MTEXT$>       = '"' : NewText : '" already exists. Please enter a different name.'
                MsgStruct<MTYPE$>       = 'BO'
                MsgStruct<MICON$>       = 'H'
                MsgStruct<MCAPTION$>    = 'HTTP Framework Setup'
                Msg(@Window, MsgStruct)
                ItemKey = 'NEWITEM'
                Set_Property(CtrlEntId, 'OLE.ItemData[' : ItemKey : ']', OldText)
                Send_Event(@Window : '.OLE_PUB_RENAME', 'OLE', 'OnClick', '', 'Left', False$, False$)
            end
            Set_Property(@Window : '.OLE_PUB_CREATE_APIS', 'ENABLED', True$)
        end
    end else
        // Create a new ItemKey using the original item's parent and the new item's text.
        If ItemParentIsRoot NE True$ then
            ItemKey     = ItemParent
            GoSub GetURLPath
            If ItemClass EQ 'RESOURCE_ID' then
                // Make sure the resource ID is surrounded by curly braces.
                If NewText[1, 1] NE '{' then NewText = '{' : NewText
                If NewText[-1, 1] NE '}' then NewText := '}'
                Set_Property(CtrlEntId, 'OLE.Item[' : OrigItemKey : ']', NewText)
            end
            ItemKey     = URLPath : '/' : NewText
            Convert '.' to '_' in ItemKey
            Item    = Get_Property(CtrlEntId, 'OLE.Item[' : ItemKey : ']')
            If Item EQ '' then
                // Rename the original ItemKey to the new ItemKey. Update the API Signatures associated with this resource.
                Send_Message(CtrlEntId, 'OLE.ChangeKeys', OrigItemKey, ItemKey)
                APISignatures   = Get_Property(CtrlEntId, 'OLE.ItemField[' : ItemKey : ';API_SIGNATURES]')
                Swap OldText with NewText in APISignatures
                Set_Property(CtrlEntId, 'OLE.ItemField[' : ItemKey : ';API_SIGNATURES]', APISignatures)
                Send_Event(CtrlEntId, 'OLE', 'RenameItemChildren', ItemKey : '')
                Set_Property(@Window : '.OLE_PUB_CREATE_APIS', 'ENABLED', True$)
            end else
                If NewText NE OldText then
                    // User is renaming an item to one that already exists. Put this back in edit mode.
                    MsgStruct               = ''
                    MsgStruct<MTEXT$>       = '"' : NewText : '" already exists. Please enter a different name.'
                    MsgStruct<MTYPE$>       = 'BO'
                    MsgStruct<MICON$>       = 'H'
                    MsgStruct<MCAPTION$>    = 'HTTP Framework Setup'
                    Msg(@Window, MsgStruct)
                    ItemKey = OrigItemKey
                    Set_Property(CtrlEntId, 'OLE.ItemData[' : ItemKey : ']', OldText)
                    Send_Event(@Window : '.OLE_PUB_RENAME', 'OLE', 'OnClick', '', 'Left', False$, False$)
                    Set_Property(@Window : '.OLE_PUB_CREATE_APIS', 'ENABLED', True$)
                end
            end
        end else
            // The API Root is being renamed. Update the API URL text.
            NewText = '/' : NewText
            Set_Property(@Window : '.EDL_API_URL', 'TEXT', NewText)
            Send_Event(CtrlEntId, 'OLE', 'RenameItemChildren', ItemKey : '')
            Set_Property(@Window : '.OLE_PUB_CREATE_APIS', 'ENABLED', True$)
        end
    end

    // Set the focus on the new item and update the fully qualified URL path in the form based on the updated item.
    Set_Property(CtrlEntId, 'OLE.SelectedItems', ItemKey)
    // Recursively call the service to make sure all dependent children are renamed.
    Set_Property(CtrlEntId, 'OLE.FocusItem', ItemKey)
    // Trigger the OnSelChange event handler to update other control properties.
    Send_Event(CtrlEntId, 'OLE', 'OnSelChange', ItemKey : '')

end event


Event OLE_TRE_RESOURCES.RenameItemChildren(ItemKey)

    If ItemKey NE '' then
        ItemChildren    = Get_Property(CtrlEntId, 'OLE.ItemChildren[' : ItemKey : ']')
        If ItemChildren NE '' then
            ItemParent      = ItemKey
            For Each ItemKey in ItemChildren using @FM
                // Save the original item key and its current text.
                OrigItemKey = ItemKey
                ItemText    = Get_Property(CtrlEntId, 'OLE.ItemData[' : OrigItemKey : ']')
                // Create a new URL path for the parent item.
                ItemKey     = ItemParent
                GoSub GetURLPath
                // Create a new item key using the parent's URL path and the current text.
                ItemKey     = URLPath : '/' : ItemText
                Convert '.' to '_' in ItemKey
                // Rename the original ItemKey to the new ItemKey.
                Send_Message(CtrlEntId, 'OLE.ChangeKeys', OrigItemKey, ItemKey)
                // Recursively call the service to make sure all dependent children are renamed.
                Send_Event(CtrlEntId, 'OLE', 'RenameItemChildren', ItemKey : '')
            Next ItemKey
        end
    end

end event


Event OLE_TRE_RESOURCES.OnContextMenuClick(Item, UserData)

    ItemKey     = UserData
    Set_Property(CtrlEntId, 'OLE.SelectedItems', ItemKey)
    // Trigger the OnSelChange event handler to update other control properties.
    Send_Event(CtrlEntId, 'OLE', 'OnSelChange', ItemKey : '')
    Begin Case
        Case Item EQ 'RENAME'
            Send_Event(@Window : '.OLE_PUB_RENAME', 'OLE', 'OnClick', '', 'Left', False$, False$)
        Case Item EQ 'REMOVE'
            Send_Event(@Window : '.OLE_PUB_REMOVE_ENDPOINT', 'OLE', 'OnClick', '', 'Left', False$, False$)
        Case Item[1, 3] EQ 'NEW'
            Begin Case
                Case Item EQ 'NEW_RESOURCE'
                    Send_Event(@Window : '.OLE_PUB_NEW_RESOURCE', 'OLE', 'OnClick', '', 'Left', False$, False$)
                Case Item EQ 'NEW_RESOURCE_ID'
                    Send_Event(@Window : '.OLE_PUB_NEW_RESOURCE_ID', 'OLE', 'OnClick', '', 'Left', False$, False$)
                Case Item EQ 'NEW_PROPERTY'
                    Send_Event(@Window : '.OLE_PUB_NEW_PROPERTY', 'OLE', 'OnClick', '', 'Left', False$, False$)
            End Case
    End Case

end event


Event OLE_TRE_RESOURCES.OnDrop(SourceCtrl, SourceItemKeys, DestItemKey, Action, Shift, Ctrl)

    DestItemKey = Field(DestItemKey, '/', 1, 2) ; // Only need the parent resource.
    If DestItemKey _NEC 'APIROOT' then
        // Only allow resource items to be sorted among themselves.
*        Send_Message(CtrlEntID, 'OLE.SendItems' : @FM : 1, SourceItemKeys : @FM : CtrlEntID : @FM : DestItemKey : @FM : Action : @FM : True$ : @FM : True$)
        Send_Message(CtrlEntID, 'OLE.MoveItems', SourceItemKeys, DestItemKey, Action)
    end

end event


Event OLE_TRE_RESOURCE_METHODS.OnItemCheck(ItemKey, Checked)

    Swap 'GET / HEAD' with 'GET' in ItemKey
    APISignature    = ''
    ResourceItemKey = Get_Property(@Window : '.OLE_TRE_RESOURCES', 'OLE.SelectedItems')
    ResourceClass   = Get_Property(@Window : '.OLE_TRE_RESOURCES', 'OLE.ItemClass[' : ResourceItemKey : ']')
    ResourceName    = Get_Property(@Window : '.OLE_TRE_RESOURCES', 'OLE.ItemData[' : ResourceItemKey : ']')
    EndpointPattern = ResourceItemKey
    If EndpointPattern[1, 1] EQ '/' then EndpointPattern = EndpointPattern[2, 999]
    Convert '/' to @FM in EndpointPattern
    NumSegments     = DCount(EndpointPattern, @FM)
    Begin Case
        Case (ResourceClass EQ 'APIROOT') OR (ResourceClass EQ 'RESOURCE')
            If ResourceClass EQ 'APIROOT' then
                APISignature    = ResourceClass
            end else
                APISignature    = ResourceName
            end
        Case ResourceClass EQ 'RESOURCE_ID'
            APISignature    = EndpointPattern<NumSegments - 1> : '.ID'
        Case ResourceClass EQ 'PROPERTY'
            If EndpointPattern<NumSegments - 1>[1, 1] EQ '{' then
                APISignature    = EndpointPattern<NumSegments - 2> : '.ID.' : EndpointPattern<NumSegments>
            end else
                APISignature    = EndpointPattern<NumSegments - 1> : '.' : EndpointPattern<NumSegments>
            end
    End Case
    APISignature   := '.' : ItemKey
    Methods         = Get_Property(@Window : '.OLE_TRE_RESOURCES', 'OLE.ItemField[' : ResourceItemKey : ';METHODS]')
    APISignatures   = Get_Property(@Window : '.OLE_TRE_RESOURCES', 'OLE.ItemField[' : ResourceItemKey : ';API_SIGNATURES]')
    Convert ',' to @FM in Methods
    Convert ',' to @FM in APISignatures
    If Checked EQ True$ then
        Locate ItemKey in Methods using @FM setting fPos else
            Methods         = Insert(Methods, fPos, 0, 0, ItemKey)
            APISignatures   = Insert(APISignatures, fPos, 0, 0, APISignature)
        end
        Set_Property(@Window : '.OLE_PUB_CREATE_APIS', 'ENABLED', True$)
    end else
        Locate ItemKey in Methods using @FM setting fPos then
            Methods         = Delete(Methods, fPos, 0, 0)
            APISignatures   = Delete(APISignatures, fPos, 0, 0)
        end
    end
    Convert @FM to ',' in Methods
    Convert @FM to ',' in APISignatures
    Set_Property(@Window : '.OLE_TRE_RESOURCES', 'OLE.ItemField[' : ResourceItemKey : ';METHODS]', Methods)
    Set_Property(@Window : '.OLE_TRE_RESOURCES', 'OLE.ItemField[' : ResourceItemKey : ';API_SIGNATURES]', APISignatures)

    GoSub UpdateSetupInfo

end event


Event OLE_TRE_RESOURCE_QUERY_PARAMS.OnSelChange(Selection)

    ItemKey     = Selection
    Set_Property(CtrlEntId, '@PREV_ITEMKEY', ItemKey)   ; // Used by LOSTFOCUS event handlers that might be triggered when the user changes the item selected.
    Set_Property(@Window : '.OLE_PUB_RESOURCE_QUERY_PARAMS_REMOVE', 'ENABLED', True$)

end event


Event OLE_TRE_RESOURCE_QUERY_PARAMS.AfterUpdate(ItemKey, NewText, OldText)

    // When the user renames a resource item, this needs to update the ItemKey in the Tree control. This means creating
    // a new item based on the new resource name and then removing the original item.
    Item        = Get_Property(CtrlEntId, 'OLE.Item[' : ItemKey : ']')
    ItemParent  = Get_Property(CtrlEntId, 'OLE.ItemParent[' : ItemKey : ']')
    OrigItemKey = ItemKey
    ItemKey     = NewText

    If OrigItemKey EQ 'NEWITEM' then
        Locate NewText in '[Query Param]' using @FM setting Pos then
            // User did not change the default new resource name so remove it.
            Send_Message(CtrlEntId, 'OLE.RemoveItems', OrigItemKey)
        end else
            Item    = Get_Property(CtrlEntId, 'OLE.Item[' : ItemKey : ']')
            If Item EQ '' then
                // Rename the original ItemKey to the new ItemKey.
                Send_Message(CtrlEntId, 'OLE.ChangeKeys', OrigItemKey, ItemKey)
                ResourceItemKey = Get_Property(@Window : '.OLE_TRE_RESOURCES', 'OLE.SelectedItems')
                QueryParams     = Get_Property(@Window : '.OLE_TRE_RESOURCES', 'OLE.ItemField[' : ResourceItemKey : ';QUERY_PARAMS]')
                Convert ',' to @FM in QueryParams
                Locate OrigItemKey in QueryParams using @FM setting fPos then QueryParams = Delete(QueryParams, fPos, 0, 0)
                Locate ItemKey in QueryParams using @FM setting fPos else QueryParams = Insert(QueryParams, fPos, 0, 0, ItemKey)
                Convert @FM to ',' in QueryParams
                Set_Property(@Window : '.OLE_TRE_RESOURCES', 'OLE.ItemField[' : ResourceItemKey : ';QUERY_PARAMS]', QueryParams)
                GoSub UpdateSetupInfo
            end else
                // User created an item that already exists. Put this back in edit mode.
                MsgStruct               = ''
                MsgStruct<MTEXT$>       = '"' : NewText : '" already exists. Please enter a different name.'
                MsgStruct<MTYPE$>       = 'BO'
                MsgStruct<MICON$>       = 'H'
                MsgStruct<MCAPTION$>    = 'HTTP Framework Setup'
                Msg(@Window, MsgStruct)
                ItemKey = 'NEWITEM'
                Set_Property(CtrlEntId, 'OLE.ItemData[' : ItemKey : ']', OldText)
                Send_Event(CtrlEntId, 'OLE', 'OnDblClick', ItemKey : '', '', 'Left', False$, False$)
            end
        end
    end else
        // Create a new ItemKey using the original item's parent and the new item's text.
        Item    = Get_Property(CtrlEntId, 'OLE.Item[' : ItemKey : ']')
        If Item EQ '' then
            // Rename the original ItemKey to the new ItemKey.
            Send_Message(CtrlEntId, 'OLE.ChangeKeys', OrigItemKey, ItemKey)
            ResourceItemKey = Get_Property(@Window : '.OLE_TRE_RESOURCES', 'OLE.SelectedItems')
            QueryParams     = Get_Property(@Window : '.OLE_TRE_RESOURCES', 'OLE.ItemField[' : ResourceItemKey : ';QUERY_PARAMS]')
            Convert ',' to @FM in QueryParams
            Locate OrigItemKey in QueryParams using @FM setting fPos then QueryParams = Delete(QueryParams, fPos, 0, 0)
            Locate ItemKey in QueryParams using @FM setting fPos else QueryParams = Insert(QueryParams, fPos, 0, 0, ItemKey)
            Convert @FM to ',' in QueryParams
            Set_Property(@Window : '.OLE_TRE_RESOURCES', 'OLE.ItemField[' : ResourceItemKey : ';QUERY_PARAMS]', QueryParams)
            GoSub UpdateSetupInfo
        end else
            If NewText NE OldText then
                // User is renaming an item to one that already exists. Put this back in edit mode.
                MsgStruct               = ''
                MsgStruct<MTEXT$>       = '"' : NewText : '" already exists. Please enter a different name.'
                MsgStruct<MTYPE$>       = 'BO'
                MsgStruct<MICON$>       = 'H'
                MsgStruct<MCAPTION$>    = 'HTTP Framework Setup'
                Msg(@Window, MsgStruct)
                ItemKey = OrigItemKey
                Set_Property(CtrlEntId, 'OLE.ItemData[' : ItemKey : ']', OldText)
                Send_Event(CtrlEntId, 'OLE', 'OnDblClick', ItemKey : '', '', 'Left', False$, False$)
            end
        end
    end

    Set_Property(CtrlEntId, 'OLE.SelectedItems', ItemKey)
    Set_Property(CtrlEntId, 'OLE.FocusItem', ItemKey)
    Send_Event(CtrlEntId, 'OLE', 'OnSelChange', ItemKey : '')

end event


Event OLE_TRE_RESOURCE_QUERY_PARAMS.OnDblClick(ItemKey, Point, Button, Shift, Ctrl)

    If (ItemKey NE '') AND (Button EQ 'Left') then
        Send_Message(CtrlEntId, 'OLE.EditItem', ItemKey, False$, False$, True$)
    end

end event


Event EDL_RESOURCE_TITLE.LOSTFOCUS(Flag, FocusID)

    GotFocusValue   = Get_Property(CtrlEntId, 'GOTFOCUS_VALUE')
    Text            = Get_Property(CtrlEntId, 'TEXT')
    If GotFocusValue NE Text then
        ResourceItemKey = Get_Property(@Window : '.OLE_TRE_RESOURCES', '@PREV_ITEMKEY')
        Set_Property(@Window : '.OLE_TRE_RESOURCES', 'OLE.ItemField[' : ResourceItemKey : ';TITLE]', Text)
    end

end event


Event EDB_RESOURCE_DESCRIPTION.LOSTFOCUS(Flag, FocusID)

    GotFocusValue   = Get_Property(CtrlEntId, 'GOTFOCUS_VALUE')
    Text            = Get_Property(CtrlEntId, 'TEXT')
    If GotFocusValue NE Text then
        ResourceItemKey = Get_Property(@Window : '.OLE_TRE_RESOURCES', '@PREV_ITEMKEY')
        Set_Property(@Window : '.OLE_TRE_RESOURCES', 'OLE.ItemField[' : ResourceItemKey : ';DESCRIPTION]', Text)
    end

end event


Event CHB_RESOURCE_EXCLUDE_FROM_LOGGING.CLICK()

    Check           = Get_Property(CtrlEntId, 'CHECK')
    ResourceItemKey = Get_Property(@Window : '.OLE_TRE_RESOURCES', '@PREV_ITEMKEY')
    Set_Property(@Window : '.OLE_TRE_RESOURCES', 'OLE.ItemField[' : ResourceItemKey : ';EXCLUDE_LOGGING]', Check)

end event


Event OLE_PUB_NEW_RESOURCE.OnClick(Point, Button, Shift, Ctrl)

    ItemKey             = Get_Property(@Window : '.OLE_TRE_RESOURCES', 'OLE.SelectedItems')
    NewItemKey          = 'NEWITEM'
    NewItem             = ''
    NewItem<0, 1>       = '1'
    NewItem<0, 2>       = NewItemKey
    NewItem<0, 3>       = '[Resource]'
    NewItem<0, 5>       = 'RESOURCE'
    NewItem<0, 19, 1>   = 'METHODS' : @TM : ''
    NewItem<0, 19, 2>   = 'QUERY_PARAMS' : @TM : ''
    NewItem<0, 19, 3>   = 'DESCRIPTION' : @TM : ''
    NewItem<0, 19, 4>   = 'TITLE' : @TM : ''
    NewItem<0, 19, 5>   = 'EXCLUDE_LOGGING' : @TM : False$
    Send_Message(@Window : '.OLE_TRE_RESOURCES', 'OLE.AddItems', ItemKey, NewItem)
    Set_Property(@Window : '.OLE_TRE_RESOURCES', 'OLE.SelectedItems', NewItemKey)
    Send_Message(@Window : '.OLE_TRE_RESOURCES', 'OLE.EditItem', NewItemKey, False$, False$, True$)
    // Trigger the OnSelChange event handler to update other control properties.
    Send_Event(@Window : '.OLE_TRE_RESOURCES', 'OLE', 'OnSelChange', ItemKey : '')

end event


Event OLE_PUB_NEW_RESOURCE_ID.OnClick(Point, Button, Shift, Ctrl)

    ItemKey             = Get_Property(@Window : '.OLE_TRE_RESOURCES', 'OLE.SelectedItems')
    NewItemKey          = 'NEWITEM'
    NewItem             = ''
    NewItem<0, 1>       = '1'
    NewItem<0, 2>       = NewItemKey
    NewItem<0, 3>       = '{Resource ID}'
    NewItem<0, 5>       = 'RESOURCE_ID'
    NewItem<0, 19, 1>   = 'METHODS' : @TM : ''
    NewItem<0, 19, 2>   = 'QUERY_PARAMS' : @TM : ''
    NewItem<0, 19, 3>   = 'DESCRIPTION' : @TM : ''
    NewItem<0, 19, 4>   = 'TITLE' : @TM : ''
    NewItem<0, 19, 5>   = 'EXCLUDE_LOGGING' : @TM : False$
    Send_Message(@Window : '.OLE_TRE_RESOURCES', 'OLE.AddItems', ItemKey, NewItem)
    Set_Property(@Window : '.OLE_TRE_RESOURCES', 'OLE.SelectedItems', NewItemKey)
    Send_Message(@Window : '.OLE_TRE_RESOURCES', 'OLE.EditItem', NewItemKey, False$, False$, True$)
    // Trigger the OnSelChange event handler to update other control properties.
    Send_Event(@Window : '.OLE_TRE_RESOURCES', 'OLE', 'OnSelChange', ItemKey : '')

end event


Event OLE_PUB_NEW_PROPERTY.OnClick(Point, Button, Shift, Ctrl)

    ItemKey             = Get_Property(@Window : '.OLE_TRE_RESOURCES', 'OLE.SelectedItems')
    NewItemKey          = 'NEWITEM'
    NewItem             = ''
    NewItem<0, 1>       = '1'
    NewItem<0, 2>       = NewItemKey
    NewItem<0, 3>       = '<Property>'
    NewItem<0, 5>       = 'PROPERTY'
    NewItem<0, 19, 1>   = 'METHODS' : @TM : ''
    NewItem<0, 19, 2>   = 'QUERY_PARAMS' : @TM : ''
    NewItem<0, 19, 3>   = 'DESCRIPTION' : @TM : ''
    NewItem<0, 19, 4>   = 'TITLE' : @TM : ''
    NewItem<0, 19, 5>   = 'EXCLUDE_LOGGING' : @TM : False$
    Send_Message(@Window : '.OLE_TRE_RESOURCES', 'OLE.AddItems', ItemKey, NewItem)
    Set_Property(@Window : '.OLE_TRE_RESOURCES', 'OLE.SelectedItems', NewItemKey)
    Send_Message(@Window : '.OLE_TRE_RESOURCES', 'OLE.EditItem', NewItemKey, False$, False$, True$)
    // Trigger the OnSelChange event handler to update other control properties.
    Send_Event(@Window : '.OLE_TRE_RESOURCES', 'OLE', 'OnSelChange', ItemKey : '')

end event


Event OLE_PUB_RENAME.OnClick(Point, Button, Shift, Ctrl)

    ItemKey = Get_Property(@Window : '.OLE_TRE_RESOURCES', 'OLE.SelectedItems')
    Send_Message(@Window : '.OLE_TRE_RESOURCES', 'OLE.EditItem', ItemKey, False$, False$, True$)

end event


Event OLE_PUB_REMOVE_ENDPOINT.OnClick(Point, Button, Shift, Ctrl)

    ItemKey                 = Get_Property(@Window : '.OLE_TRE_RESOURCES', 'OLE.SelectedItems')
    ItemText                = Get_Property(@Window : '.OLE_TRE_RESOURCES', 'ItemData[' : ItemKey : ']')
    MsgStruct               = ''
    MsgStruct<MTEXT$>       = 'Removing "' : ItemText : '" cannot be undone. Are you sure you want to remove it?'
    MsgStruct<MTYPE$>       = 'BNY'
    MsgStruct<MICON$>       = '?'
    MsgStruct<MDEFBTN$>     = 2
    MsgStruct<MCAPTION$>    = 'HTTP Framework Setup'
    ConfirmRemove           = Msg(@Window, MsgStruct)
    If ConfirmRemove EQ True$ then
        ItemParent  = Get_Property(@Window : '.OLE_TRE_RESOURCES', 'ItemParent[' : ItemKey : ']')
        Send_Message(@Window : '.OLE_TRE_RESOURCES', 'OLE.RemoveItems', ItemKey)
        Transfer ItemParent to ItemKey
        Set_Property(@Window : '.OLE_TRE_RESOURCES', 'OLE.SelectedItems', ItemKey)
        Set_Property(@Window : '.OLE_TRE_RESOURCES', 'OLE.FocusItem', ItemKey)
        // Trigger the OnSelChange event handler to update other control properties.
        Send_Event(@Window : '.OLE_TRE_RESOURCES', 'OLE', 'OnSelChange', ItemKey : '')
    end

end event


Event OLE_PUB_CREATE_APIS.OnClick(Point, Button, Shift, Ctrl)

    MakeLocal               = Get_Property(CtrlEntId, '@MAKELOCAL')
    DisplayMessage          = True$ ; // Assume the final message will be displayed.
    ItemList                = Get_Property(@Window : '.OLE_TRE_RESOURCES', 'OLE.ItemList')
    APIsUpdated             = HTTP_Services('UpdateWebAPIs', ItemList, MakeLocal)
    MsgStruct               = ''
    MsgStruct<MCAPTION$>    = 'SRP HTTP Framework'

    If Error_Services('HasError') then
        Message = Error_Services('GetMessage')
        If Index(Message, REP_APP_INHERIT_MOD_ERR$, 1) then
            // Attempting to update an inherited Web API. Ask if a local copy should be made instead.
            Error_Services('Clear')         ; // Clear the message stack since it is being handled in a special way.
            MsgStruct<MTYPE$>       = 'BNY'
            MsgStruct<MICON$>       = '?'
            SysReposKeyID           = Message[-1, 'B ']
            AppID                   = SysReposKeyID[1, '*']
            TypeID                  = SysReposKeyID[Col2() + 1, '*']
            ClassID                 = SysReposKeyID[Col2() + 1, '*']
            EntID                   = SysReposKeyID[Col2() + 1, '*']
            MsgStruct<MTEXT$>       = EntID : ' is inherited from ' : AppID : ' and cannot be updated. Would you like to make a copy in the local application?'
            MakeCopy                = Msg(@Window, MsgStruct)
            If MakeCopy EQ True$ then
                Set_Property(CtrlEntId, '@MAKELOCAL', True$)
                Send_Event(CtrlEntID, 'OLE', 'OnClick', Point : '', Button : '', Shift : '', Ctrl : '')
                DisplayMessage  = False$    ; // Do not display the final message since it will be handled in the recursive event.
            end
        end
    end

    If DisplayMessage EQ True$ then
        MsgStruct<MTYPE$>       = 'BO'
        If Error_Services('HasError') then
            MsgStruct<MICON$>       = 'H'
            MsgStruct<MTEXT$>       = Error_Services('GetMessage')
        end else
            If APIsUpdated EQ True$ then
                MsgStruct<MICON$>       = '*'
                MsgStruct<MTEXT$>       = 'New APIs have been created!'
            end else
                MsgStruct<MICON$>       = '*'
                MsgStruct<MTEXT$>       = 'No new APIs needed to be created.'
            end
            Set_Property(CtrlEntId, 'ENABLED', False$)
        end
        Msg(@Window, MsgStruct)
    end

    // Turn off the MakeLocal flag to prevent accidental attempts to localize an inherited API.
    Set_Property(CtrlEntId, '@MAKELOCAL', False$)

end event


Event OLE_PUB_RESOURCE_QUERY_PARAMS_NEW.OnClick(Point, Button, Shift, Ctrl)

    ItemKey             = 'NEWITEM'
    NewItem             = ''
    NewItem<0, 1>       = '1'
    NewItem<0, 2>       = ItemKey
    NewItem<0, 3>       = '[Query Param]'
    NewItem<0, 5>       = 'QUERY_PARAM'
    Send_Message(@Window : '.OLE_TRE_RESOURCE_QUERY_PARAMS', 'OLE.AddItems', '', NewItem)
    Set_Property(@Window : '.OLE_TRE_RESOURCE_QUERY_PARAMS', 'OLE.SelectedItems', ItemKey)
    // Trigger the OnSelChange event handler to update other control properties.
*    Send_Event(@Window : '.OLE_TRE_RESOURCE_QUERY_PARAMS', 'OLE', 'OnSelChange', ItemKey)
    Send_Message(@Window : '.OLE_TRE_RESOURCE_QUERY_PARAMS', 'OLE.EditItem', ItemKey, False$, False$, True$)

end event


Event OLE_PUB_RESOURCE_QUERY_PARAMS_REMOVE.OnClick(Point, Button, Shift, Ctrl)

    ItemKey                 = Get_Property(@Window : '.OLE_TRE_RESOURCE_QUERY_PARAMS', 'OLE.SelectedItems')
    ItemText                = Get_Property(@Window : '.OLE_TRE_RESOURCE_QUERY_PARAMS', 'ItemData[' : ItemKey : ']')
    MsgStruct               = ''
    MsgStruct<MTEXT$>       = 'Removing "' : ItemText : '" cannot be undone. Are you sure you want to remove it?'
    MsgStruct<MTYPE$>       = 'BNY'
    MsgStruct<MICON$>       = '?'
    MsgStruct<MDEFBTN$>     = 2
    MsgStruct<MCAPTION$>    = 'HTTP Framework Setup'
    ConfirmRemove           = Msg(@Window, MsgStruct)
    If ConfirmRemove EQ True$ then
        Send_Message(@Window : '.OLE_TRE_RESOURCE_QUERY_PARAMS', 'OLE.RemoveItems', ItemKey)
        ResourceItemKey = Get_Property(@Window : '.OLE_TRE_RESOURCES', 'OLE.SelectedItems')
        QueryParams     = Get_Property(@Window : '.OLE_TRE_RESOURCES', 'OLE.ItemField[' : ResourceItemKey : ';QUERY_PARAMS]')
        Convert ',' to @FM in QueryParams
        Locate ItemKey in QueryParams using @FM setting fPos then QueryParams = Delete(QueryParams, fPos, 0, 0)
        Convert @FM to ',' in QueryParams
        Set_Property(@Window : '.OLE_TRE_RESOURCES', 'OLE.ItemField[' : ResourceItemKey : ';QUERY_PARAMS]', QueryParams)
        Set_Property(CtrlEntId, 'ENABLED', False$)
        GoSub UpdateSetupInfo
    end

end event


Event OLE_PUB_CONTRACT.OnClick(Point, Button, Shift, Ctrl)

    // Collapse all resource tree items and then expand the APIROOT so only the top-level resources are seen.
    Set_Property(@Window : '.OLE_TRE_RESOURCES', 'OLE.ItemExpanded[All]', False$)
    Set_Property(@Window : '.OLE_TRE_RESOURCES', 'OLE.ItemExpanded[APIROOT]', True$)

end event


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Internal Gosubs
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

SetupOLEControls:

    // All OLE controls can use this qualify configuration.
    Qualify     = ''
    Qualify<1>  = 1
    Qualify<3>  = ''
    Qualify<4>  = 0

    //------------------------------------------------------------------------------------------------------------------
    //
    //  Left side menu (SRP Tree Control)
    //
    //  The styling configuration is designed to mimic Office 2016's Option dialog.
    //
    //------------------------------------------------------------------------------------------------------------------
    Ctrl        = @Window : '.OLE_TRE_MENU'
    Set_Property(Ctrl, 'OLE.Border', 'Flat')
    Set_Property(Ctrl, 'OLE.DefFont', 'Segoe UI' : @SVM : '8' : @SVM : '100')
    Set_Property(Ctrl, 'OLE.DefColors', 'RGB(88,88,88)' : @FM : 'White')
    Set_Property(Ctrl, 'OLE.DefHeight', '25')
    Colors      = ''
    Colors<1>   = 'Black' : @VM : 'RGB(197,197,197)'
    Colors<2>   = 'Black' : @VM : 'RGB(197,197,197)'
    Colors<3>   = 'Black' : @VM : 'Vertical(Gradient(RGB(197,197,197), RGB(197,197,197)), Border(RGB(155,155,155)))'
    Colors<4>   = 'Black' : @VM : 'RGB(197,197,197)'
    Set_Property(Ctrl, 'OLE.Colors', Colors)
    Set_Property(Ctrl, 'OLE.FullRowSelect', True$)
    Set_Property(Ctrl, 'OLE.LinesEnabled', False$)
    Set_Property(Ctrl, 'OLE.RootIndent', True$)
    Set_Property(Ctrl, 'OLE.Indent', '10')
    Set_Property(Ctrl, 'OLE.ShowFocusItem', False$)
    ItemList    = ''
    ItemList<1> = 1 : @VM : 'Settings' : @VM : 'Settings'
    ItemList<2> = 1 : @VM : 'Authentication' : @VM : 'Authentication'
    ItemList<3> = 1 : @VM : 'Resources' : @VM : 'Resources'
    Set_Property(Ctrl, 'OLE.ItemList', ItemList)
    Set_Property(Ctrl, 'OLE.SelectedItems', 'Settings')
    Send_Message(Ctrl, 'QUALIFY_EVENT', 'OLE.OnSelChange', Qualify)


    //------------------------------------------------------------------------------------------------------------------
    //
    //  Right side main borders (SRP Tab Control)
    //
    //  The styling configuration is designed to mimic Office 2016's Option dialog.
    //
    //------------------------------------------------------------------------------------------------------------------
    TabCtrls    = 'OLE_TAB,OLE_TAB_RESOURCE_METADATA'
    NumTabs     = DCount(TabCtrls, ',')
    For TabCnt = 1 to NumTabs
        Ctrl            = @Window : '.' : Field(TabCtrls, ',', TabCnt, 1)
        Set_Property(Ctrl, 'OLE.AllowXPTheme', False$)
        Set_Property(Ctrl, 'OLE.CustomPaneColor', 'Vertical(White, White, Border(RGB(160,160,160)))')
        Set_Property(Ctrl, 'OLE.FlickerFree', True$)
    Next TabCnt
    Set_Property(@Window : '.OLE_TAB', 'OLE.Height', '-34')
    Set_Property(@Window : '.OLE_TAB_RESOURCE_METADATA', 'OLE.Height', '-96')


    //------------------------------------------------------------------------------------------------------------------
    //
    //  Command buttons (SRP Button Control)
    //
    //  The styling configuration is designed to mimic Office 2016's Option dialog.
    //
    //------------------------------------------------------------------------------------------------------------------
    ButtonCtrls     = 'OLE_PUB_NEW_RESOURCE,OLE_PUB_NEW_RESOURCE_ID,OLE_PUB_NEW_PROPERTY,OLE_PUB_RENAME,OLE_PUB_REMOVE_ENDPOINT,OLE_PUB_CREATE_APIS,OLE_PUB_RESOURCE_QUERY_PARAMS_NEW,OLE_PUB_RESOURCE_QUERY_PARAMS_REMOVE'
    ToolTips        = 'Creates a new resource that is subordinate to the current resource.' : @FM
    ToolTips       := 'Creates a new resource ID for the current resource.' : @FM
    ToolTips       := 'Creates a new property for the current resource.' : @FM
    ToolTips       := 'Renames the current resource, resource ID, or property.' : @FM
    ToolTips       := 'Removes the current resource, resource ID, or property.' : @FM
    ToolTips       := 'Creates new API procedures or endpoint handlers as needed.' : @FM
    ToolTips       := 'Creates a new query parameter for the current resource.' : @FM
    ToolTips       := 'Removes the current query parameter.' : @FM
    NumButtons      = DCount(ButtonCtrls, ',')
    For ButtonCnt = 1 to NumButtons
        Ctrl            = @Window : '.' : Field(ButtonCtrls, ',', ButtonCnt, 1)
        Set_Property(Ctrl, 'OLE.BackVisible', False$)
        ToolTip         = ToolTips<ButtonCnt>
        Set_Property(Ctrl, 'OLE.ToolTip', ToolTip)
        Forecolor       = ''
        Forecolor<1>    = 'RGB(68,68,68)'
        Forecolor<2>    = 'RGB(68,68,68)'
        Forecolor<3>    = 'RGB(68,68,68)'
        Forecolor<4>    = 'RGB(171,171,171)'
        Forecolor<5>    = 'RGB(68,68,68)'
        Set_Property(Ctrl, 'OLE.Forecolor', Forecolor)
        Background      = ''
        Background<1>   = 'Vertical(Gradient(RGB(253,253,253), RGB(253,253,253)), Border(RGB(171,171,171)))'
        Background<2>   = 'Vertical(Gradient(RGB(213,225,242), RGB(213,225,242)), Border(RGB(163,189,227)))'
        Background<3>   = 'Vertical(Gradient(RGB(163,189,227), RGB(163,189,227)), Border(RGB(120,153,203)))'
        Background<4>   = 'Vertical(Gradient(RGB(253,253,253), RGB(253,253,253)), Border(RGB(171,171,171)))'
        Background<5>   = 'Vertical(Gradient(RGB(253,253,253), RGB(253,253,253)), Border(RGB(171,171,171)))'
        Set_Property(Ctrl, 'OLE.Background', Background)
        Send_Message(Ctrl, 'QUALIFY_EVENT', 'OLE.OnClick', Qualify)
    Next ButtonCnt
    Set_Property(@Window : '.OLE_PUB_NEW_RESOURCE', 'OLE.IconList', ApplicationPath$ : '\BMPS\' : 'SRPHTTPResource.png' : @VM : 'L5' : @VM : 'C')
    Set_Property(@Window : '.OLE_PUB_NEW_RESOURCE_ID', 'OLE.IconList', ApplicationPath$ : '\BMPS\' : 'SRPHTTPResourceID.png' : @VM : 'L5' : @VM : 'C')
    Set_Property(@Window : '.OLE_PUB_NEW_PROPERTY', 'OLE.IconList', ApplicationPath$ : '\BMPS\' : 'SRPHTTPProperty.png' : @VM : 'L5' : @VM : 'C')
    Set_Property(@Window : '.OLE_PUB_RENAME', 'OLE.IconList', ApplicationPath$ : '\BMPS\' : 'SRPHTTPRename.png' : @VM : 'L5' : @VM : 'C')
    Set_Property(@Window : '.OLE_PUB_REMOVE_ENDPOINT', 'OLE.IconList', ApplicationPath$ : '\BMPS\' : 'SRPHTTPRemove.png' : @VM : 'L5' : @VM : 'C')
    Set_Property(@Window : '.OLE_PUB_CREATE_APIS', 'OLE.IconList', ApplicationPath$ : '\BMPS\' : 'SRPHTTPCreateAPIs.png' : @VM : 'L5' : @VM : 'C')
    Set_Property(@Window : '.OLE_PUB_NEW_RESOURCE', 'OLE.Caption', '')
    Set_Property(@Window : '.OLE_PUB_NEW_RESOURCE_ID', 'OLE.Caption', '')
    Set_Property(@Window : '.OLE_PUB_NEW_PROPERTY', 'OLE.Caption', '')
    Set_Property(@Window : '.OLE_PUB_RENAME', 'OLE.Caption', '')
    Set_Property(@Window : '.OLE_PUB_REMOVE_ENDPOINT', 'OLE.Caption', '')
    Set_Property(@Window : '.OLE_PUB_CREATE_APIS', 'OLE.Caption', '')
    Set_Property(@Window : '.OLE_PUB_NEW_RESOURCE', 'OLE.CaptionList', 'New Resource' : @VM : 'L25' : @VM : 'C')
    Set_Property(@Window : '.OLE_PUB_NEW_RESOURCE_ID', 'OLE.CaptionList', 'New Resource ID' : @VM : 'L25' : @VM : 'C')
    Set_Property(@Window : '.OLE_PUB_NEW_PROPERTY', 'OLE.CaptionList', 'New Property' : @VM : 'L25' : @VM : 'C')
    Set_Property(@Window : '.OLE_PUB_RENAME', 'OLE.CaptionList', 'Rename' : @VM : 'L25' : @VM : 'C')
    Set_Property(@Window : '.OLE_PUB_REMOVE_ENDPOINT', 'OLE.CaptionList', 'Remove Endpoint...' : @VM : 'L25' : @VM : 'C')
    Set_Property(@Window : '.OLE_PUB_CREATE_APIS', 'OLE.CaptionList', 'Create APIs' : @VM : 'L25' : @VM : 'C')


    //------------------------------------------------------------------------------------------------------------------
    //
    //  Horizontal group banners (SRP Picture Control)
    //
    //  The styling configuration is designed to mimic Office 2016's Option dialog.
    //
    //------------------------------------------------------------------------------------------------------------------
    PictureCtrls    = 'OLE_PIC_SERVER_OPTIONS,OLE_PIC_AUTHENTICATION_OPTIONS,OLE_PIC_RESOURCE_MANAGER'
    NumPictures     = DCount(PictureCtrls, ',')
    GroupLeftBorder = 151 + 14              ; // The distance between the left border of the panel control and a group banner.
    ItemLeftBorder  = GroupLeftBorder + 14  ; // The distance between the left border of the panel control and an item.
    ItemBuffer      = 22                    ; // The distance between the upper border of a single line item and the upper border of the next item.
    GroupVPos       = 14 * 5                ; // Group vertical position.
    GroupHeight     = 24                    ; // Height of the group banner.
    GroupBuffer     = 40                    ; // The distance between the upper border of a group banner and the upper border of the first item.
    For PictureCnt = 1 to NumPictures
        Ctrl            = @Window : '.' : Field(PictureCtrls, ',', PictureCnt, 1)
        OrigSize        = Get_Property(Ctrl, 'SIZE')
        GroupVPos       = OrigSize<2>
        Set_Property(Ctrl, 'OLE.BackgroundColor', 'RGB(238, 238, 238)')
        Set_Property(Ctrl, 'OLE.CaptionAlignment', 'Center' : @FM : 'Left')
        Set_Property(Ctrl, 'OLE.Font', 'Segoe UI' : @SVM : '10' : @SVM : '700')
        Set_Property(Ctrl, 'OLE.CaptionColor', 'RGB(119, 119, 119)')
        Set_Property(Ctrl, 'SIZE', GroupLeftBorder : @FM : GroupVPos : @FM : 916 : @FM : GroupHeight)
    Next PictureCnt
    Set_Property(@Window : '.OLE_PIC_SERVER_OPTIONS', 'OLE.Caption', '  ' : 'API Server Settings')
    Set_Property(@Window : '.OLE_PIC_AUTHENTICATION_OPTIONS', 'OLE.Caption', '  ' : 'Authentication Options')
    Set_Property(@Window : '.OLE_PIC_RESOURCE_MANAGER', 'OLE.Caption', '  ' : 'Resource Manager')


    //------------------------------------------------------------------------------------------------------------------
    //
    //  Labels with borders (SRP Picture Control)
    //
    //  The styling configuration is designed to mimic Office 2016's Option dialog.
    //
    //------------------------------------------------------------------------------------------------------------------
    PictureCtrls    = 'OLE_PIC_HTTP,OLE_PIC_SERVER_STATUS'
    NumPictures     = DCount(PictureCtrls, ',')
    For PictureCnt = 1 to NumPictures
        Ctrl            = @Window : '.' : Field(PictureCtrls, ',', PictureCnt, 1)
        Set_Property(Ctrl, 'OLE.Border', 'XP')
        Set_Property(Ctrl, 'OLE.BackgroundColor', 'RGB(238, 238, 238)')
        Set_Property(Ctrl, 'OLE.CaptionAlignment', 'Center' : @FM : 'Center')
    Next PictureCnt
    Set_Property(@Window : '.OLE_PIC_HTTP', 'OLE.Caption', 'http+https://')
    Set_Property(@Window : '.OLE_PIC_SERVER_STATUS', 'OLE.Caption', 'Server Status: ')


    //------------------------------------------------------------------------------------------------------------------
    //
    //  Labels with partial borders (SRP Picture Control)
    //
    //  The styling configuration is designed to mimic Office 2016's Option dialog.
    //
    //------------------------------------------------------------------------------------------------------------------
    PictureCtrls    = 'OLE_PIC_URL'
    NumPictures     = DCount(PictureCtrls, ',')
    For PictureCnt = 1 to NumPictures
        Ctrl            = @Window : '.' : Field(PictureCtrls, ',', PictureCnt, 1)
        Set_Property(Ctrl, 'OLE.Border', 'None')
        *Set_Property(Ctrl, 'OLE.BackgroundColor', 'RGB(238, 238, 238)')
        Set_Property(Ctrl, 'OLE.BackgroundColor', 'White')
        Set_Property(Ctrl, 'OLE.CaptionAlignment', 'Center' : @FM : 'Left')
        Set_Property(Ctrl, 'OLE.CaptionOffset', 0 : @FM : 5)
        Set_Property(Ctrl, 'OLE.Alignment', 'Center' : @FM : 'Left')
    Next PictureCnt
    APIURL  = HTTP_Services('GetAPIRootURL', True$)
    Set_Property(@Window : '.OLE_PIC_URL', 'OLE.Caption', APIURL)


    //------------------------------------------------------------------------------------------------------------------
    //
    //  Non-Authenticted Paths, Whitelisted IPs, and Banned IPs (SRP EditTable Control)
    //
    //  The styling configuration is designed to mimic Office 2016's Option dialog.
    //
    //------------------------------------------------------------------------------------------------------------------
    EditTableCtrls = 'OLE_EDT_NON_AUTHENTICATED_PATHS,OLE_EDT_WHITELISTED_IPS,OLE_EDT_BANNED_IPS'
    For Each EditTableCtrl in EditTableCtrls using ',' setting EditTableCnt
        Ctrl               = @Window : '.' : EditTableCtrl
        Begin Case
            Case EditTableCtrl EQ 'OLE_EDT_NON_AUTHENTICATED_PATHS'
                TableDimension = 2 : @FM : 1
                TitleList      = 'Path' : @VM : 'Query Params'
                PromptText1    = 'Enter a URL path without query params'
                PromptText2    = 'Query params'
            Case EditTableCtrl EQ 'OLE_EDT_WHITELISTED_IPS'
                TableDimension = 1 : @FM : 1
                TitleList      = ''
                PromptText1    = 'Enter an IP or URL authority'
                PromptText2    = ''
            Case EditTableCtrl EQ 'OLE_EDT_BANNED_IPS'
                TableDimension = 1 : @FM : 1
                TitleList      = ''
                PromptText1    = 'Enter an IP or URL authority'
                PromptText2    = ''
        End Case
        Set_Property(Ctrl, 'OLE.Dimension', TableDimension)
        Set_Property(Ctrl, 'OLE.Border', 'Flat')
        Set_Property(Ctrl, 'OLE.WorkspaceBkColor', 'White')
        If TitleList NE '' then
            Set_Property(Ctrl, 'OLE.TitleList', TitleList)
            Set_Property(Ctrl, 'OLE.HeaderRow[1]', 18 : @FM : True$)
        end else
            Set_Property(Ctrl, 'OLE.HeaderRow[1]', '' : @FM : False$)
        end
        Set_Property(Ctrl, 'OLE.HeaderFont[All; All]', 'Segoe UI' : @SVM : '9' : @SVM : '400')
        Set_Property(Ctrl, 'OLE.HeaderColumn[1]', '' : @FM : False$)
        Set_Property(Ctrl, 'OLE.DataRow[All]', 18 : @FM : True$ : @FM : False$ : @FM : False$)
        Set_Property(Ctrl, 'OLE.DataColumn[1]', 100 : @FM : True$ : @FM : False$ : @FM : True$)
        Set_Property(Ctrl, 'OLE.CellEditMode[All; All]', '' : @FM : 'Insert')
        Set_Property(Ctrl, 'OLE.CellFont[All; All]', 'Segoe UI' : @SVM : '9' : @SVM : '400')
*        Set_Property(Ctrl, 'OLE.CellGridLines[All;All]', 'None' : @FM : 'None' : @FM : 'None' : @FM : 'None')
        Set_Property(Ctrl, 'OLE.CellGridLines[All; All]', 'None' : @FM : '' : @FM : 'None' : @FM : '')
        CellColors         = ''
        CellColors<1>      = 'RGB(68,68,68)'
        CellColors<2>      = 'White'
        CellColors<3>      = 'RGB(68,68,68)'
        CellColors<4>      = 'RGB(192,224,255)'
        CellColors<5>      = 'RGB(68,68,68)'
        Set_Property(Ctrl, 'OLE.CellColors[All; All]', CellColors)
        CellColors<4>      = 'White'
        CellColors<5>      = True$
        Set_Property(Ctrl, 'OLE.HeaderColors[All; All]', CellColors)
        SelectionStyle     = ''
        SelectionStyle<1>  = 'RGB(68,68,68)' : @VM : 'RGB(192,224,255)'
        SelectionStyle<4>  = 'None'
        SelectionStyle<5>  = 0
        SelectionStyle<6>  = 'None'
        Set_Property(Ctrl, 'OLE.SelectionStyle', SelectionStyle)
        CellPromptArray    = ''
        CellPromptArray<1> = PromptText1
        CellPromptArray<2> = 'Segoe UI' : @SVM : '9' : @SVM : '100'
        CellPromptArray<4> = 'Center'
        CellPromptArray<6> = True$
        CellPromptArray<7> = 'Both'
        Set_Property(Ctrl, 'OLE.CellPrompt[1; All]', CellPromptArray)
        If PromptText2 NE '' then
            CellPromptArray<1> = PromptText2
            Set_Property(Ctrl, 'OLE.CellPrompt[2; All]', CellPromptArray)
        end
        Send_Message(Ctrl, 'QUALIFY_EVENT', 'OLE.AfterDeleteRecords', Qualify)
        Send_Message(Ctrl, 'QUALIFY_EVENT', 'OLE.AfterInsertRecords', Qualify)
        Send_Message(Ctrl, 'QUALIFY_EVENT', 'OLE.AfterNewRecords', Qualify)
        Send_Message(Ctrl, 'QUALIFY_EVENT', 'OLE.AfterUpdate', Qualify)
    Next PictureCnt
    Ctrl               = @Window : '.OLE_EDT_NON_AUTHENTICATED_PATHS'
    Set_Property(Ctrl, 'OLE.DataColumn[2]', 150 : @FM : True$ : @FM : False$ : @FM : False$)
    Set_Property(Ctrl, 'OLE.CellMultilined[2; All]', True$)
    NonAuthPaths       = SetupInfo<HTTP_FRAMEWORK_SETUP_NON_AUTHENTICATED_PATHS$>
    NonAuthQueryParams = SetupInfo<HTTP_FRAMEWORK_SETUP_NON_AUTHENTICATED_QUERY_PARAMS$>
    Array              = NonAuthPaths : @FM : NonAuthQueryParams
    Set_Property(Ctrl, 'OLE.Array', Array)
    Ctrl               = @Window : '.OLE_EDT_WHITELISTED_IPS'
    WhitelistIPs       = SetupInfo<HTTP_FRAMEWORK_SETUP_WHITELISTED_IPS$>
    Set_Property(Ctrl, 'OLE.Array', WhitelistIPs)
    Ctrl               = @Window : '.OLE_EDT_BANNED_IPS'
    BannedIPs          = SetupInfo<HTTP_FRAMEWORK_SETUP_BANNED_IPS$>
    Set_Property(Ctrl, 'OLE.Array', BannedIPs)


    //------------------------------------------------------------------------------------------------------------------
    //
    //  Resource hierarchical view (SRP Tree Control)
    //
    //  The styling configuration is designed to mimic Office 2016's Option dialog.
    //
    //------------------------------------------------------------------------------------------------------------------
    Ctrl            = @Window : '.OLE_TRE_RESOURCES'

    // Set common tree properties
    Set_Property(Ctrl, 'OLE.CtrlEntId', Ctrl)
    Set_Property(Ctrl, 'OLE.FullRowSelect', True$)
    Set_Property(Ctrl, 'OLE.ShowFocusItem', False$)
    Set_Property(Ctrl, 'OLE.LinesEnabled', False$)
    Set_Property(Ctrl, 'OLE.RootIndent', True$)
    Set_Property(Ctrl, 'OLE.ButtonsEnabled', True$)
    Set_Property(Ctrl, 'OLE.DefHeight', 24)
    Set_Property(Ctrl, 'OLE.ExpandBehavior', 'None')
    Set_Property(Ctrl, 'OLE.EnterBehavior', 'None')
    Set_Property(Ctrl, 'OLE.DragEnabled', True$)
    Set_Property(Ctrl, 'OLE.DropBehavior', 'Insert')
    Set_Property(Ctrl, 'OLE.DropSources', Ctrl)
    Set_Property(Ctrl, 'OLE.DragCondition', 'Level EQ 2')

    // Setup glyphs
    Images          = ''
    Images<1>       = ApplicationPath$ : '\BMPS\' : 'SRPHTTPTreeExpanded.png'
    Images<2>       = ApplicationPath$ : '\BMPS\' : 'SRPHTTPTreeCollapsed.png'
    Set_Property(Ctrl, 'OLE.GlyphImages', Images)

    // Set tree theme
    Colors          = ''
    Colors<1, 1>    = 'RGB(068,068,068)'
    Colors<1, 2>    = 'RGB(194,213,242)'
    Colors<2, 1>    = 'RGB(068,068,068)'
    Colors<2, 2>    = 'RGB(213,225,242)'
    Colors<3, 1>    = 'RGB(068,068,068)'
    Colors<3, 2>    = 'Vertical(Gradient(RGB(213,225,242), RGB(213,225,242)), Border(RGB(163,189,227)), Margin(0, 0, 0, 0))'
    Colors<4, 1>    = 'RGB(068,068,068)'
    Colors<4, 2>    = 'RGB(194,213,242)'
    Set_Property(Ctrl, 'OLE.Colors', Colors)

    Colors          = ''
    Colors<1>       = 'RGB(068,068,068)'
    Colors<2>       = 'White'
    Set_Property(Ctrl, 'OLE.DefColors', Colors)
    Set_Property(Ctrl, 'OLE.Border', 'None')
    Set_Property(Ctrl, 'OLE.Background', 'White')
    Set_Property(Ctrl, 'OLE.DefFont', 'Segoe UI' : @SVM : '-12' : @SVM : '400' : @SVM : '0' : @SVM : '0')
    Set_Property(Ctrl, 'OLE.ImageSize', '16x16')
    Set_Property(Ctrl, 'OLE.LinesEnabled', False$)
    Set_Property(Ctrl, 'OLE.ButtonsEnabled', True$)

    // Setup tree images
    Images          = ''
    Images<-1>      = 'APIROOT'     : @VM : ApplicationPath$ : '\BMPS\' : 'SRPHTTPEntryPoint.png'
    Images<-1>      = 'RESOURCE'    : @VM : ApplicationPath$ : '\BMPS\' : 'SRPHTTPResource.png'
    Images<-1>      = 'RESOURCE_ID' : @VM : ApplicationPath$ : '\BMPS\' : 'SRPHTTPResourceID.png'
    Images<-1>      = 'PROPERTY'    : @VM : ApplicationPath$ : '\BMPS\' : 'SRPHTTPProperty.png'
    Set_Property(Ctrl, 'OLE.Images', Images)

    // Set common image conditions
    Conditions      = ''
    Conditions<-1>  = 'APIROOT'     : @VM : 'Class EQ "APIROOT"'
    Conditions<-1>  = 'RESOURCE'    : @VM : 'Class EQ "RESOURCE"'
    Conditions<-1>  = 'RESOURCE_ID' : @VM : 'Class EQ "RESOURCE_ID"'
    Conditions<-1>  = 'PROPERTY'    : @VM : 'Class EQ "PROPERTY"'
    Set_Property(Ctrl, 'OLE.ImageConditions', Conditions)
    Send_Message(Ctrl, 'QUALIFY_EVENT', 'OLE.OnSelChange', Qualify)
    Send_Message(Ctrl, 'QUALIFY_EVENT', 'OLE.OnClick', Qualify)
    Send_Message(Ctrl, 'QUALIFY_EVENT', 'OLE.OnDblClick', Qualify)
    Send_Message(Ctrl, 'QUALIFY_EVENT', 'OLE.AfterUpdate', Qualify)
    Send_Message(Ctrl, 'QUALIFY_EVENT', 'OLE.OnContextMenuClick', Qualify)
    Send_Message(Ctrl, 'QUALIFY_EVENT', 'OLE.OnDrop', Qualify)


    //------------------------------------------------------------------------------------------------------------------
    //
    //  Resource Tree buttons (SRP Button Control)
    //
    //  The styling configuration is designed to mimic Office 2016's Option dialog.
    //
    //------------------------------------------------------------------------------------------------------------------
    ButtonCtrls     = 'OLE_PUB_CONTRACT'
    ToolTips        = 'View only top-level resources.'
    NumButtons      = DCount(ButtonCtrls, ',')
    For ButtonCnt = 1 to NumButtons
        Ctrl            = @Window : '.' : Field(ButtonCtrls, ',', ButtonCnt, 1)
        Set_Property(Ctrl, 'OLE.BackVisible', False$)
        ToolTip         = ToolTips<ButtonCnt>
        Set_Property(Ctrl, 'OLE.ToolTip', ToolTip)
        Send_Message(Ctrl, 'QUALIFY_EVENT', 'OLE.OnClick', Qualify)
    Next ButtonCnt
    Set_Property(@Window : '.OLE_PUB_CONTRACT', 'OLE.IconList', ApplicationPath$ : '\BMPS\' : 'SRPHTTPTreeTopLevelResources.png' : @VM : 'C' : @VM : 'C')
    Set_Property(@Window : '.OLE_PUB_CONTRACT', 'OLE.Caption', '')


    //------------------------------------------------------------------------------------------------------------------
    //
    //  Methods and Query Params view (SRP Tree Control)
    //
    //  The styling configuration is designed to mimic Office 2016's Option dialog.
    //
    //------------------------------------------------------------------------------------------------------------------
    TreeCtrls   = 'OLE_TRE_RESOURCE_METHODS,OLE_TRE_RESOURCE_QUERY_PARAMS'
    NumTrees    = DCount(TreeCtrls, ',')
    For TreeCnt = 1 to NumTrees
        Ctrl            = @Window : '.' : Field(TreeCtrls, ',', TreeCnt, 1)

        // Set common tree properties
        Set_Property(Ctrl, 'OLE.CtrlEntId', Ctrl)
        Set_Property(Ctrl, 'OLE.FullRowSelect', True$)
        Set_Property(Ctrl, 'OLE.ShowFocusItem', False$)
        Set_Property(Ctrl, 'OLE.LinesEnabled', False$)
        Set_Property(Ctrl, 'OLE.RootIndent', False$)
        Set_Property(Ctrl, 'OLE.ButtonsEnabled', False$)
        Set_Property(Ctrl, 'OLE.ExpandBehavior', 'None')
        Set_Property(Ctrl, 'OLE.EnterBehavior', 'None')

        // Set tree theme
        Colors          = ''
        Colors<1, 1>    = 'RGB(068,068,068)'
        Colors<1, 2>    = 'RGB(194,213,242)'
        Colors<2, 1>    = 'RGB(068,068,068)'
        Colors<2, 2>    = 'RGB(213,225,242)'
        Colors<3, 1>    = 'RGB(068,068,068)'
        Colors<3, 2>    = 'Vertical(Gradient(RGB(213,225,242), RGB(213,225,242)), Border(RGB(163,189,227)), Margin(0, 0, 0, 0))'
        Colors<4, 1>    = 'RGB(068,068,068)'
        Colors<4, 2>    = 'RGB(194,213,242)'
        Set_Property(Ctrl, 'OLE.Colors', Colors)

        Colors          = ''
        Colors<1>       = 'RGB(068,068,068)'
        Colors<2>       = 'White'
        Set_Property(Ctrl, 'OLE.DefColors', Colors)
        Set_Property(Ctrl, 'OLE.Background', 'White')
        Set_Property(Ctrl, 'OLE.DefFont', 'Segoe UI' : @SVM : '-12' : @SVM : '400' : @SVM : '0' : @SVM : '0')
        Set_Property(Ctrl, 'OLE.LinesEnabled', False$)
        Set_Property(Ctrl, 'OLE.ButtonsEnabled', False$)
    Next TreeCnt

    Methods             = 'GET / HEAD,POST,PUT,PATCH,DELETE'
    ItemList            = ''
    For Each Method in Methods using ',' setting cPos
        ItemList<cPos, 1>   = 1
        ItemList<cPos, 2>   = Method
        ItemList<cPos, 3>   = Method
        ItemList<cPos, 11>  = 'Left'
    Next Method
    Set_Property(@Window : '.OLE_TRE_RESOURCE_METHODS', 'OLE.DefHeight', 24)
    Set_Property(@Window : '.OLE_TRE_RESOURCE_METHODS', 'OLE.ItemList', ItemList)
    Set_Property(@Window : '.OLE_TRE_RESOURCE_METHODS', 'OLE.DefSelectable', False$)
    Send_Message(@Window : '.OLE_TRE_RESOURCE_METHODS', 'QUALIFY_EVENT', 'OLE.OnItemCheck', Qualify)
    Set_Property(@Window : '.OLE_TRE_RESOURCE_QUERY_PARAMS', 'OLE.DefSelectable', True$)
    Set_Property(@Window : '.OLE_TRE_RESOURCE_QUERY_PARAMS', 'OLE.Prompts', 'There are no query params.')
    Send_Message(@Window : '.OLE_TRE_RESOURCE_QUERY_PARAMS', 'QUALIFY_EVENT', 'OLE.OnSelChange', Qualify)
    Send_Message(@Window : '.OLE_TRE_RESOURCE_QUERY_PARAMS', 'QUALIFY_EVENT', 'OLE.OnClick', Qualify)
    Send_Message(@Window : '.OLE_TRE_RESOURCE_QUERY_PARAMS', 'QUALIFY_EVENT', 'OLE.OnDblClick', Qualify)
    Send_Message(@Window : '.OLE_TRE_RESOURCE_QUERY_PARAMS', 'QUALIFY_EVENT', 'OLE.AfterUpdate', Qualify)
    Send_Message(@Window : '.OLE_TRE_RESOURCE_QUERY_PARAMS', 'QUALIFY_EVENT', 'OLE.OnContextMenuClick', Qualify)


    //------------------------------------------------------------------------------------------------------------------
    //
    //  Info help (SRP Picture Control)
    //
    //  The styling configuration is designed to mimic Office 2016's Option dialog.
    //
    //------------------------------------------------------------------------------------------------------------------
    AllOLECtrls     = Utility('OBJECTLIST', @Window, 'OLECONTROL')
    Image           = ApplicationPath$ : '\BMPS\' : 'SRPHTTPInfo.png'
    NumOLECtrls     = DCount(AllOLECtrls, @FM)
    For OLECnt = 1 to NumOLECtrls
        Ctrl    = AllOLECtrls<OLECnt>
        If Index(Ctrl, '.OLE_INFO_', 1) then
            Set_Property(Ctrl, 'OLE.Image', Image)
            Send_Message(Ctrl, 'QUALIFY_EVENT', 'OLE.OnMouseEnter', Qualify)
            Send_Message(Ctrl, 'QUALIFY_EVENT', 'OLE.OnMouseExit', Qualify)
        end
    Next PictureCnt

return


UpdateSetupInfo:

    // Get the Setup information from the server and then update based on the data in the form.
    LocalSetupKeyID                                                    = HTTP_Services('GetLocalAppKeyID', SetupRowKeyID$)
    SetupInfo                                                          = Database_Services('ReadDataRow', SetupTable$, LocalSetupKeyID)
    SetupInfo<HTTP_FRAMEWORK_SETUP_HOME_URL$>                          = Get_Property(@Window : '.EDL_HOME_URL', 'TEXT')
    SetupInfo<HTTP_FRAMEWORK_SETUP_API_URL$>                           = Get_Property(@Window : '.EDL_API_URL', 'TEXT')
    SetupInfo<HTTP_FRAMEWORK_SETUP_API_CALL_PROCEDURE$>                = Get_Property(@Window : '.COB_API_CALL_PROCEDURE', 'TEXT')
    SetupInfo<HTTP_FRAMEWORK_SETUP_ENABLE_LOGGING$>                    = Get_Property(@Window : '.CHB_ENABLE_LOGGING', 'CHECK')
    SetupInfo<HTTP_FRAMEWORK_SETUP_LOG_ERRORS_ONLY$>                   = Get_Property(@Window : '.CHB_LOG_ERRORS_ONLY', 'CHECK')
    SetupInfo<HTTP_FRAMEWORK_SETUP_CAPTURE_PATH$>                      = Get_Property(@Window : '.EDL_CAPTURE_PATH', 'TEXT')
    SetupInfo<HTTP_FRAMEWORK_SETUP_REALM_VALUE$>                       = Get_Property(@Window : '.EDL_REALM_VALUE', 'TEXT')
    SetupInfo<HTTP_FRAMEWORK_SETUP_ENTRY_POINT_SERVICE$>               = Get_Property(@Window : '.EDL_ENTRY_POINT_SERVICE', 'TEXT')
    SetupInfo<HTTP_FRAMEWORK_SETUP_ABORTED_SERVICE$>                   = Get_Property(@Window : '.EDL_ABORTED_SERVICE', 'TEXT')
    SettingText                                                        = Get_Property(@Window : '.COB_DEBUGGER_SETTING', 'TEXT')
    Locate SettingText in 'Disabled,Enabled,Intercept' using ',' setting DebuggerSetting else DebuggerSetting = 1
    DebuggerSetting                                                   -= 1
    SetupInfo<HTTP_FRAMEWORK_SETUP_DEBUGGER_SETTING$>                  = DebuggerSetting
    SetupInfo<HTTP_FRAMEWORK_SETUP_DEBUGGER_INTERCEPT$>                = Get_Property(@Window : '.EDL_DEBUGGER_INTERCEPT', 'TEXT')
    SetupInfo<HTTP_FRAMEWORK_SETUP_FLUSH_CACHE$>                       = Get_Property(@Window : '.CHB_FLUSH_CACHE', 'CHECK')
    SetupInfo<HTTP_FRAMEWORK_SETUP_ENABLE_AUTHENTICATION$>             = Get_Property(@Window : '.CHB_ENABLE_AUTHENTICATION', 'CHECK')
    SetupInfo<HTTP_FRAMEWORK_SETUP_ENABLE_HTTP_BASIC_AUTHENTICATION$>  = Get_Property(@Window : '.CHB_ENABLE_HTTP_BASIC_AUTHENTICATION', 'CHECK')
    SetupInfo<HTTP_FRAMEWORK_SETUP_NEW_PASSWORD_TIME_TO_LIVE$>         = Get_Property(@Window : '.EDL_NEW_PASSWORD_TIME_TO_LIVE', 'TEXT')
    SetupInfo<HTTP_FRAMEWORK_SETUP_OLD_PASSWORD_TIME_TO_LIVE$>         = Get_Property(@Window : '.EDL_OLD_PASSWORD_TIME_TO_LIVE', 'TEXT')
    SetupInfo<HTTP_FRAMEWORK_SETUP_INVALID_PASSWORD_LIMIT$>            = Get_Property(@Window : '.EDL_INVALID_PASSWORD_LIMIT', 'TEXT')
    SetupInfo<HTTP_FRAMEWORK_SETUP_CONTAINMENT_ACTION$>                = Get_Property(@Window : '.COB_CONTAINMENT_ACTION', 'TEXT')
    Array                                                              = Get_Property(@Window : '.OLE_EDT_NON_AUTHENTICATED_PATHS', 'OLE.Array')
    SetupInfo<HTTP_FRAMEWORK_SETUP_NON_AUTHENTICATED_PATHS$>           = Array<1>
    SetupInfo<HTTP_FRAMEWORK_SETUP_NON_AUTHENTICATED_QUERY_PARAMS$>    = Array<2>
    SetupInfo<HTTP_FRAMEWORK_SETUP_WHITELISTED_IPS$>                   = Get_Property(@Window : '.OLE_EDT_WHITELISTED_IPS', 'ARRAY')
    SetupInfo<HTTP_FRAMEWORK_SETUP_WHITELISTED_IPS_TYPE$>              = Get_Property(@Window : '.RAG_WHITELISTED_IPS', 'VALUE')
    SetupInfo<HTTP_FRAMEWORK_SETUP_BANNED_IPS$>                        = Get_Property(@Window : '.OLE_EDT_BANNED_IPS', 'ARRAY')
    // Save the new Setup information to the server.
    Database_Services('WriteDataRow', SetupTable$, LocalSetupKeyID, SetupInfo, True$, False$, False$)

    If Error_Services('HasError') then
        MsgStruct               = ''
        MsgStruct<MTEXT$>       = Error_Services('GetMessage')
        MsgStruct<MTYPE$>       = 'BO'
        MsgStruct<MICON$>       = 'H'
        MsgStruct<MCAPTION$>    = 'SRP HTTP Framework'
        Msg(@Window, MsgStruct)
    end else
        // Save the new Server Enabled information to the server.
        // Get the resource map from the SRP Tree control's ItemList property.
        LocalSetupKeyID = HTTP_Services('GetLocalAppKeyID', ResourcesKeyID$)
        CurrItemList    = Database_Services('ReadDataRow', SetupTable$, LocalSetupKeyID)
        ItemList        = Get_Property(@Window : '.OLE_TRE_RESOURCES', 'OLE.ItemList')
        If CurrItemList NE ItemList then
            Database_Services('WriteDataRow', SetupTable$, LocalSetupKeyID, ItemList, True$, False$, False$)
            If Error_Services('HasError') then
                MsgStruct               = ''
                MsgStruct<MTEXT$>       = Error_Services('GetMessage')
                MsgStruct<MTYPE$>       = 'BO'
                MsgStruct<MICON$>       = 'H'
                MsgStruct<MCAPTION$>    = 'SRP HTTP Framework'
                Msg(@Window, MsgStruct)
            end
        end
    end

    // Since changes have been made, clear the cache so other processses can get fresh data.
    Memory_Services('ReleaseHashTable', CacheName$)

    ItemKey = Get_Property(@Window : '.OLE_TRE_RESOURCES', 'OLE.SelectedItems')
    GoSub UpdateURLPath

return


TransferParams:

    // ActiveX controls pass their own event names through Param1. Modify the parameter values so they conform to
    // OpenInsight event parameter values. This will allow commuter modules to be structured the same for OpenInsight
    // event and ActiveX (OLE) events.
    Transfer Param1  to Event
    Transfer Param2  to Param1
    Transfer Param3  to Param2
    Transfer Param4  to Param3
    Transfer Param5  to Param4
    Transfer Param6  to Param5
    Transfer Param7  to Param6
    Transfer Param8  to Param7
    Transfer Param9  to Param8
    Transfer Param10 to Param9
    Transfer Param11 to Param10
    Transfer Param12 to Param11
    Transfer Param13 to Param12
    Transfer Param14 to Param13
    Transfer Param15 to Param14

return


RestoreParams:

    // Restore the event parameters so the rest of the event chain will see the parameter values as they were originally
    // created by OpenInsight. This will also prevent the parameter values from being transferred multiple times in case
    // there are multiple OLE promoted event handlers (e.g. APPNAME*..OIWIN* and APPNAME*OLE..OIWIN*).
    Transfer Param14 to Param15
    Transfer Param13 to Param14
    Transfer Param12 to Param13
    Transfer Param11 to Param12
    Transfer Param10 to Param11
    Transfer Param9  to Param10
    Transfer Param8  to Param9 
    Transfer Param7  to Param8 
    Transfer Param6  to Param7
    Transfer Param5  to Param6
    Transfer Param4  to Param5
    Transfer Param3  to Param4
    Transfer Param2  to Param3
    Transfer Param1  to Param2
    Transfer Event   to Param1
    Event             = 'OLE'

return


DisplayInfoHelp:

    InfoTitle   = ''

    Begin Case
        Case Control _EQC 'OLE_INFO_HOME_URL'
            InfoTitle   = 'Home URL'
            InfoText    = 'The Home URL for the web site'
            InfoText   := @FM : @FM
            InfoText   := 'The home URL (domain) for the API being requested. Normally this is the same URL that directs '
            InfoText   := 'the user to a home page. The framework will automatically derive this from the request, but '
            InfoText   := 'a default URL should be entered in case one cannot be derived.'
            InfoText   := @FM : @FM
            InfoText   := '"http://" or "https://" may be specified as apart of the URL, but it is recommended to leave '
            InfoText   := 'it out so the API can dynamically add it in based on the HTTP request details.'

        Case Control _EQC 'OLE_INFO_API_URL'
            InfoTitle   = 'API URL'
            InfoText    = 'The API end point'
            InfoText   := @FM : @FM
            InfoText   := 'The end point where all API requests begin. This should not include the Home URL. For example, '
            InfoText   := 'if the full API URL is "http://www.example.org/api" then the Home URL should be set to "www.example.org" '
            InfoText   := 'and the API URL should be set to "/api".'

        Case Control _EQC 'OLE_INFO_API_CALL_PROCEDURE'
            InfoTitle   = 'API Call Procedure'
            InfoText    = 'The procedure method used to call the API'
            InfoText   := @FM : @FM
            InfoText   := 'There are two procedure methods that can call an API. The classic method is "HTTP Service". This method '
            InfoText   := 'calls an HTTP service procedure for each resource segment in the URL. HTTP service procedures are always '
            InfoText   := 'responsible for validating the HTTP method, properties, and sub-resources. The current method is "Web API". '
            InfoText   := 'This method creates a map for the current URL to a specific API procedure and calls it. HTTP method, '
            InfoText   := 'properties, and sub-resources are all validated in common code automatically so the developer no longer has to '
            InfoText   := 'handle this in each API procedure.'

        Case Control _EQC 'OLE_INFO_ENABLE_LOGGING'
            InfoTitle   = 'Enable Logging?'
            InfoText    = 'Tells the server if logging is enabled'
            InfoText   := @FM : @FM
            InfoText   := 'This is used by the CreateLogFile service.'

        Case Control _EQC 'OLE_INFO_LOG_ERRORS_ONLY'
            InfoTitle   = 'Log Errors Only'
            InfoText    = 'Tells the server if only error responses should be logged'
            InfoText   := @FM : @FM
            InfoText   := 'If set, only error responses (i.e., status codes of 4xx or 5xx) will be logged. This is used '
            InfoText   := 'by the CreateLogFile service.'

        Case Control _EQC 'OLE_INFO_CAPTURE_PATH'
            InfoTitle   = 'Capture Path'
            InfoText    = 'Path for all logs to be captured'
            InfoText   := @FM : @FM
            InfoText   := 'Used in HTTP_MCP to identify the local OS path where request and response content can be saved '
            InfoText   := 'for off-line analysis. If the path does not exist, then no attempt to store this information will be made.'

        Case Control _EQC 'OLE_INFO_REALM_VALUE'
            InfoTitle   = 'Realm Value'
            InfoText    = 'The HTTP Realm Value'
            InfoText   := @FM : @FM
            InfoText   := 'Used with HTTP authentication to associate resource links to the same protective space.'

        Case Control _EQC 'OLE_INFO_ENTRY_POINT_SERVICE'
            InfoTitle   = 'Entry Point Service'
            InfoText    = 'Tells the server which HTTP service handles the API entry point'
            InfoText   := @FM : @FM
            InfoText   := 'If the HTTP Service API calling procedure type is used, this is the name of the entry point web '
            InfoText   := 'service that will be called by HTTP_MCP as the first web service.'

        Case Control _EQC 'OLE_INFO_ABORTED_SERVICE'
            InfoTitle   = 'Aborted Service'
            InfoText    = 'Tells the server how to aborted HTTP requests'
            InfoText   := @FM : @FM
            InfoText   := 'Service handler for HTTP Requests that get aborted either due to runtime errors or status errors. '
            InfoText   := 'If the Debugger Intercept has been enabled, this handler will only receive Status Error aborts.'

        Case Control _EQC 'OLE_INFO_DEBUGGER_SETTING'
            InfoTitle   = 'Debugger Setting'
            InfoText    = 'Tells the server how to handle debug or runtime error conditions'
            InfoText   := @FM : @FM
            InfoText   := 'Enabled means to allow the interactive debugger to respond to debug statements or runtime errors. '
            InfoText   := 'Disabled means to suppress any activity that would normally bring up the interactive debugger. '
            InfoText   := 'Intercept means to call the indicated stored procedure to handle the runtime error condition.'

        Case Control _EQC 'OLE_INFO_FLUSH_CACHE'
            InfoTitle   = 'Flush Cache'
            InfoText    = 'Tells the server to clear all cached code when finished'
            InfoText   := @FM : @FM
            InfoText   := 'This allows changes in web service procedures to always be available without having to restart '
            InfoText   := 'the OEngineServer.'

        Case Control _EQC 'OLE_INFO_SERVER_ENABLED'
            InfoTitle   = 'Server Status'
            InfoText    = 'Sets the enabled status of the server'
            InfoText   := @FM : @FM
            InfoText   := 'If the server is disabled, no API requests will be permitted. The server can be disabled '
            InfoText   := 'by clicking the button if it has the caption "Disable Server". If the caption says "Enable Server", '
            InfoText   := 'then clicking it will re-enable the server so long as other concerns, such as breach containment '
            InfoText   := 'actions, do not disable the server again.'
            InfoText   := @FM : @FM
            InfoText   := 'The status message displays the current enable status of the server and will dynamically update '
            InfoText   := 'itself every 5 seconds.'

        Case Control _EQC 'OLE_INFO_ENABLE_AUTHENTICATION'
            InfoTitle   = 'Enable Authentication?'
            InfoText    = 'Tells the server to enforce authentication'
            InfoText   := @FM : @FM
            InfoText   := 'If set, authentication is enforced. The type of authentication methods enforced will depend upon '
            InfoText   := 'other settings. If this is not set, no authentication is enforced. Authentication should only '
            InfoText   := 'be disabled for testing purposes or when authentication processes are still being configured.'

        Case Control _EQC 'OLE_INFO_ENABLE_HTTP_BASIC_AUTHENTICATION'
            InfoTitle   = 'Enable HTTP Basic Authentication?'
            InfoText    = 'Tells the server to enforce HTTP Basic Authentication'
            InfoText   := @FM : @FM
            InfoText   := 'If set, HTTP Basic Authentication is enforced. A valid database table name and current password '
            InfoText   := 'password column must be defined. This setting is ignored if Enable Authentication is not set.'

        Case Control _EQC 'OLE_INFO_NEW_PASSWORD_TIME_TO_LIVE'
            InfoTitle   = 'New Password Time to Live'
            InfoText    = 'Length of time new passwords are valid'
            InfoText   := @FM : @FM
            InfoText   := 'If HTTP Basic Authentication is enabled, this is how long (in hours) new passwords that are '
            InfoText   := 'created can be valid before needing to be reset. If left empty, passwords do not expire.'

        Case Control _EQC 'OLE_INFO_OLD_PASSWORD_TIME_TO_LIVE'
            InfoTitle   = 'Old Password Time to Live'
            InfoText    = 'Length of time old passwords are valid'
            InfoText   := @FM : @FM
            InfoText   := 'If HTTP Basic Authentication is enabled, this is how long (in hours) old passwords can remain '
            InfoText   := 'valid. This should be a short period of time allowing for the new password to propogate. If '
            InfoText   := 'left empty, old passwords will only be valid for 1 hour.'

        Case Control _EQC 'OLE_INFO_INVALID_PASSWORD_LIMIT'
            InfoTitle   = 'Invalid Password Limit'
            InfoText    = 'Number in invalid password attempts before containment action'
            InfoText   := @FM : @FM
            InfoText   := 'If HTTP Basic Authentication is enabled, this indicates the number of invalid password attempts '
            InfoText   := 'can be attempted before containment action takes place.'

        Case Control _EQC 'OLE_INFO_TOTAL_INVALID_PASSWORD_ATTEMPTS'
            InfoTitle   = 'Total Invalid Password Attempts'
            InfoText    = 'Total number of invalid password attempts'
            InfoText   := @FM : @FM
            InfoText   := 'This indicates the total number of invalid password attempts being made to the server. This can '
            InfoText   := 'be reset by clicking the Reset button. This is reset automatically. Clicking the OK or Apply button '
            InfoText   := 'is not necessary.'

        Case Control _EQC 'OLE_INFO_CONTAINMENT_ACTION'
            InfoTitle   = 'Containment Action'
            InfoText    = 'Action to take to contain a suspected system breach'
            InfoText   := @FM : @FM
            InfoText   := 'If HTTP Basic Authentication is enabled, this indicates what action to take if the number of '
            InfoText   := 'attempted passwords fails more than the limit. The Quarantine Account option will flag this account '
            InfoText   := 'as being disabled until re-enabled. The Disable Server option will disable all connections to the '
            InfoText   := 'server until the system access is re-established.'

        Case Control _EQC 'OLE_INFO_NON_AUTHENTICATED_PATHS'
            InfoTitle  = 'Non-Authenticated Paths'
            InfoText   = 'List of URL paths that should never be authenticated'
            InfoText  := @FM : @FM
            InfoText  := 'Some paths (or endpoints) should never be authenticated because they are used only by trusted '
            InfoText  := 'connections. Paths that support OAuth redirects are a good example sinice these requests will '
            InfoText  := 'unlikely be authenticated (although they should be secret and secure with the OAuth '
            InfoText  := 'authenticating server).'
            InfoText  := @FM : @FM
            InfoText  := 'If the path should be limited to an endpoint containing one or more specified query params,'
            InfoText  := 'then enter these params in the second column.'
            InfoText  := @FM : @FM
            InfoText  := 'This should not include the Home URL or the API URL. For example, if the full non-authenticated '
            InfoText  := 'URL is "http://www.example.org/api/oauth" then the Home URL should be set to "www.example.org", '
            InfoText  := 'the API URL should be set to "/api", and the Non-Authenticated Path should be set to "/oauth".'

        Case Control _EQC 'OLE_INFO_WHITELISTED_IPS'
            InfoTitle  = 'Whitelisted IPs'
            InfoText   = 'List of IP addresses that are given special treatment when connecting to the server'
            InfoText  := @FM : @FM
            InfoText  := 'The IPs on this list will be treated differently based on other settings. In all cases, an '
            InfoText  := 'empty list will have no affect on how the remote IP address is treated.'
            InfoText  := @FM : @FM
            InfoText  := 'If "Only allow Whitelisted IPs" is selected, only those IPs listed will be permitted to connect ' 
            InfoText  := 'to the server. If "Always allow Whitelisted IPs" is selected, then those IPs listed will be '
            InfoText  := 'permitted even if the IP appears on the ban list.'
            InfoText  := @FM : @FM
            InfoText  := 'Whitelisted IPs support wildcard symbols (e.g., 127.0.0.*) or ranges (e.g., 127.0.0.1-100). '
            InfoText  := 'Note, even if the IP is permitted, other authentication rules might still be applied.'

        Case Control _EQC 'OLE_INFO_BANNED_IPS'
            InfoTitle  = 'Banned IPs'
            InfoText   = 'List of IP addresses that are banned from the server'
            InfoText  := @FM : @FM
            InfoText  := 'Even if the IP is whitelisted, this list will have the final jurisdiction over the IP. '
            InfoText  := 'Banned IPs support wildcard symbols (e.g., 127.0.0.*) or ranges (e.g., 127.0.0.1-100).'

        Case Control _EQC 'OLE_INFO_RESOURCES'
            InfoTitle   = 'Resources'
            InfoText    = 'Hierarchical view of resources'
            InfoText   := @FM : @FM
            InfoText   := 'The tree view allows resources, resource IDs, and properties to be created, renamed, or removed '
            InfoText   := 'using the command buttons at the bottom or by context menus. Clicking on a specific item in the '
            InfoText   := 'tree view automatically enables the appropriate command buttons as well as updates the URL '
            InfoText   := 'preview line above. Additional resource meta data, such as the description, supported HTTP methods '
            InfoText   := 'and supported query params, are displayed on the right.'
            InfoText   := @FM : @FM
            InfoText   := 'When the the OK or Apply button is clicked, API service logic will either be created or updated '
            InfoText   := 'as appropriate.'

        Case Control _EQC 'OLE_INFO_RESOURCE_TITLE'
            InfoTitle   = 'Resource Title'
            InfoText    = 'Title of this resource. This can be queried and used as the title of the link relationship.'

        Case Control _EQC 'OLE_INFO_RESOURCE_DESCRIPTION'
            InfoTitle   = 'Resource Description'
            InfoText    = 'Short description of this resource.'

        Case Control _EQC 'OLE_INFO_RESOURCE_METHODS'
            InfoTitle   = 'Resource Methods'
            InfoText    = 'Methods supported by this resource.'

        Case Control _EQC 'OLE_INFO_RESOURCE_QUERY_PARAMS'
            InfoTitle   = 'Resource Query Params'
            InfoText    = 'Query params supported by this resource. Click the "+" button to add a new query param. Select '
            InfoText   := 'a query param and click the "-" button to remove it. Double-click on a query param to rename it.'
            InfoText   := @FM : @FM
            InfoText   := 'If several query params are supported (such as arbitrary resource property names), then just '
            InfoText   := 'add a new query param using the "*" character.'

        Case Control _EQC 'OLE_INFO_RESOURCE_EXCLUDE_FROM_LOGGING'
            InfoTitle   = 'Exclude From Logging'
            InfoText    = 'By default, a request and response for a resource endpoint is logged (assuming logging is '
            InfoText   := 'enabled). An endpoint can be excluded from logging if this is checked.'

    End Case

    If InfoTitle NE '' then
        PopupCtrl   = @Window : '.OLE_POPUP'

        // Using the InfoTitle and InfoText content, calculate the values needed to determine the property width and
        // height of the SRP Popup control.
        TitleLen    = Len(InfoTitle)
        CoreFont    = 'Segoe UI' : @SVM : '9'
        ItemWidth   = SRP_String('GetWidth', InfoTitle, CoreFont : @SVM : '700')
        // Minimum width will be 300 pixels but it can be wider if the title requires it.
        If ItemWidth LT 300 then ItemWidth = 300
        TitleHeight = SRP_String('GetHeight', InfoTitle, CoreFont : @SVM : '700', ItemWidth, @FM) + 4
        TextHeight  = SRP_String('GetHeight', InfoText, CoreFont : @SVM : '400', ItemWidth, @FM) + 4

        ItemCnt     = 0
        LeftMarg    = 11
        VertPos     = 11                        ; // The first item will be 11 pixels from the top of the SRP Popup display.

        // Begin calculation the specific x and y coordinates for the SRP Popup display based on the control's
        // information and control type. This will be used in the ShowAt method.
        CtrlSize    = SRP_Get_Window_Rect(@Window : '.' : Control)
        CtrlWidth   = CtrlSize<3>
        CtrlHeight  = CtrlSize<4>
        CtrlXpos    = CtrlSize<1>
        CtrlYpos    = CtrlSize<2>

        // Get x and y+h of control for x & y of popup
        ShowAtX     = CtrlXpos
        // Move to the right by 10 pixels.
        ShowAtX    += 10

        // Start with the bottom of the info control.
        ShowAtY     = CtrlYpos + CtrlHeight
        // Lower the by 10 pixels.
        ShowAtY    += 10

        // Create the items to be displayed in the SRP Popup control.
        ItemList    = ''

        TextColor   = 'RGB(93, 93, 93)'
        BackColor   = 'Vertical(Gradient(White, White), Border(' : TextColor : '))'

        // Add the InfoTitle.
        ItemCnt             += 1
        ItemList<ItemCnt, 1> = LeftMarg : @SVM : VertPos : @SVM : ItemWidth : @SVM : TitleHeight
        ItemList<ItemCnt, 2> = InfoTitle
        ItemList<ItemCnt, 3> = TextColor
        ItemList<ItemCnt, 4> = CoreFont : @SVM : '700'
        ItemList<ItemCnt, 5> = 'Left' : @SVM : 'Top'

        VertPos             += TitleHeight  ; // Increase the total vertical position by the height of the info title.
        VertPos             += 5            ; // Add another 5 pixels to give space between the title and the text.

        // Add the InfoText.
        Swap @FM with \0D0A\ in InfoText
        ItemCnt             += 1
        ItemList<ItemCnt, 1> = LeftMarg : @SVM : VertPos : @SVM : ItemWidth : @SVM : TextHeight
        ItemList<ItemCnt, 2> = InfoText
        ItemList<ItemCnt, 3> = TextColor
        ItemList<ItemCnt, 4> = CoreFont : @SVM : '400'
        ItemList<ItemCnt, 5> = 'Left' : @SVM : 'Top'

        VertPos             += TextHeight   ; // Increase the total vertical position by the height of the info text.
        VertPos             += 11           ; // Add another 11 pixels to give a bottom border.

        // Set the Theme property to Custom so the special background settings will work as expected.
        Set_Property(PopupCtrl, 'OLE.Theme', 'Custom')
        Set_Property(PopupCtrl, 'OLE.Background', BackColor : @FM : 'None' : @FM : 'None')

        // Set the ItemList property.
        Set_Property(PopupCtrl, 'OLE.ItemList', ItemList)

        // Keep the SRP Popup display up indefinitely.
        Set_Property(PopupCtrl, 'OLE.ShowDelay', 0)

        GoSub HideInfoHelp

        // Set the Size of the SRP Popup display to be the full width and height needed to contain all items.
        Set_Property(PopupCtrl, 'OLE.Size', 0 : @FM : 0 : @FM : LeftMarg * 2 + ItemWidth : @FM : VertPos)

        // Finally, display the SRP Popup control.
        Send_Message(PopupCtrl, 'OLE.ShowAt', ShowAtX, ShowAtY)
    end

return


HideInfoHelp:

    PopupCtrl   = @Window : '.OLE_POPUP'
    Send_Message(PopupCtrl, 'OLE.Close')

return


//----------------------------------------------------------------------------------------------------------------------
//
//  Check the server enabled status and update the UI accordingly.
//
//----------------------------------------------------------------------------------------------------------------------
CheckServerStatus:

    ServerEnabledFlag   = HTTP_Services('GetServerEnabled')
    If ServerEnabledFlag EQ True$ then
        ButtonCaption   = 'Disable Server'
        Caption         = 'Server Status: ENABLED'
        CaptionColor    = 'Green'
    end else
        ButtonCaption   = 'Enable Server'
        Caption         = 'Server Status: DISABLED'
        CaptionColor    = 'Red'
    end

    Set_Property(@Window : '.PUB_SERVER_STATUS', 'TEXT', ButtonCaption)
    Set_Property(@Window : '.OLE_PIC_SERVER_STATUS', 'OLE.Caption', Caption)
    Set_Property(@Window : '.OLE_PIC_SERVER_STATUS', 'OLE.CaptionColor', CaptionColor)

return


//----------------------------------------------------------------------------------------------------------------------
//
//  Create a Home URL based on the data in the form.
//
//----------------------------------------------------------------------------------------------------------------------
GetHomeURL:

    HomeURL = Get_Property(@Window : '.EDL_HOME_URL', 'TEXT')
    If HomeURL[1, 4] _NEC 'http' then
        HomeURL = 'http://' : HomeURL
    end

return


//----------------------------------------------------------------------------------------------------------------------
//
//  Using the current ItemKey, create a full URL path representing this endpoint.
//
//----------------------------------------------------------------------------------------------------------------------
GetFullURLPath:

    URLPath = ''

    If ItemKey NE '' then
        Loop
            ItemParentIsRoot    = Get_Property(@Window : '.OLE_TRE_RESOURCES', 'OLE.ItemParentIsRoot[' : ItemKey : ']')
        Until ItemParentIsRoot EQ True$
            ItemData    = Get_Property(@Window : '.OLE_TRE_RESOURCES', 'OLE.ItemData[' : ItemKey : ']')
            URLPath     = Insert(URLPath, 1, 0, 0, '/' : ItemData)
            ItemKey     = Get_Property(@Window : '.OLE_TRE_RESOURCES', 'OLE.ItemParent[' : ItemKey : ']')
        Repeat
        GoSub GetHomeURL
        URLPath     = Insert(URLPath, 1, 0, 0, HomeURL : '/' : Get_Property(@Window : '.OLE_TRE_RESOURCES', 'OLE.ItemData[APIROOT]'))
    end

    Convert @FM to '' in URLPath

return


//----------------------------------------------------------------------------------------------------------------------
//
//  Using the current ItemKey, create a URL path representing this endpoint. This URL path will not include the Home or
//  API portion of the URL.
//
//----------------------------------------------------------------------------------------------------------------------
GetURLPath:

    URLPath = ''

    If ItemKey NE '' then
        Loop
            ItemParentIsRoot    = Get_Property(@Window : '.OLE_TRE_RESOURCES', 'OLE.ItemParentIsRoot[' : ItemKey : ']')
        Until ItemParentIsRoot EQ True$
            ItemData    = Get_Property(@Window : '.OLE_TRE_RESOURCES', 'OLE.ItemData[' : ItemKey : ']')
            URLPath     = Insert(URLPath, 1, 0, 0, '/' : ItemData)
            ItemKey     = Get_Property(@Window : '.OLE_TRE_RESOURCES', 'OLE.ItemParent[' : ItemKey : ']')
        Repeat
*        GoSub GetHomeURL
*        URLPath     = Insert(URLPath, 1, 0, 0, HomeURL : '/' : Get_Property(@Window : '.OLE_TRE_RESOURCES', 'OLE.ItemData[APIROOT]'))
    end

    Convert @FM to '' in URLPath

return


//----------------------------------------------------------------------------------------------------------------------
//
//  Refresh the display of the URL path in the SRP Picture control.
//
//----------------------------------------------------------------------------------------------------------------------
UpdateURLPath:

    GoSub GetFullURLPath
    Set_Property(@Window : '.OLE_PIC_URL', 'OLE.Caption', URLPath)

return
