Parsing the XML Request

When a client application sends an XML request to the Project Data Service (PDS) in Microsoft® Office Project Server 2003, if the PDS doesn't recognize the request, it looks for a registered PDS extender that implements the requested method. If the PDS finds an extender with the requested method, it then checks whether the extender implements a public method named XMLRequestEx or XMLRequest. If the public method is XMLRequestEx, the PDS calls the extender with the new syntax for Project Server 2003; otherwise, the PDS uses the legacy style syntax for Project Server 2002.

The Microsoft Visual Basic® 6.0 project in the Project 2003: Project Renamer PDS Extender (pj11pdsxRenamer.exe) download is ProjectRenamer.vbp; it contains only one class, CMain (CMain.cls), which implements the public functions XMLRequestEx and RenameProject and the private helper functions.

The Public XMLRequestEx Function

Following is the XMLRequestEx signature.

Public Function XMLRequestEx( _
    ByVal sXML As String, _
    ByVal sPDSInfoEx As String, _
    ByRef nHandled As Integer) _
    As String

XMLRequestEx parses the sPDSInfoEx parameter into member variables for the user logon, path, and database information. The sPDSInfoEx parameter contains an XML string with the following information; the SOAPRequestCookie and HTTPRequestCreate elements are optional, depending on whether the request arrived by SOAP or HTTP POST.

<PDSExtensionXML>
    <UserName></UserName>
    <UserGUID></UserGUID>
    <UserID></UserID>
    <PDSConnect></PDSConnect> 
    <BasePath></BasePath>
    <DBType></DBType>
    <SOAPRequestCookie></SOAPRequestCookie>    
    <HTTPRequestCreate></HTTPRequestCreate>    
</PDSExtensionXML>

For more information on the XMLRequestEx function and parameters, see Writing a PDS Extender.

XMLRequestEx loads and then calls the private XMLRequest function, as follows:

XMLRequestEx = XMLRequest(sXML, m_sUserName, m_sPDSConnect, m_eDatabaseType, nHandled)

The Private XMLRequest Function

XMLRequest validates and parses the ProjectRename method request, and also calls InitPjSvrSecurity to initialize the Project Server Security object (m_oPjSvrSecurity) with the BasePath value.

Following is the XMLRequest signature.

Private Function XMLRequest( _
    ByVal sXML As String, _
     ByVal sUser As String, _
     ByVal sConnect As String, _
     ByVal lDBType As Long, _
     ByRef nHandled As Integer) _
     As String

The primary parameter is sXML, which contains the XML request string. For the RenameProject method, this request takes the following form; there can be one or more Project elements:

<Request>
  <ProjectRename>
     <Project>
       <ProjectID></ProjectID>
       <NewName></NewName>
     </Project>
     <Project>
       <ProjectID></ProjectID>
       <NewName></NewName>
     </Project>
     . . .
  </ProjectRename>
</Request>

XMLRequest uses the Microsoft XML Parser (MSXML) to check if the XML request is valid:

    Dim lStatus As Long
    Dim sXMLReply As String
    Dim oXMLDocument As MSXML2.DOMDocument
    Dim oXMLRoot As MSXML2.IXMLDOMElement
    
    lStatus = STATUS_OK
    If Len(sXML) = 0 Then
        sXMLReply = "<Error>Request string is empty</Error>"
        lStatus = STATUS_INVALID_REQUEST
        GoTo PrepareReply
    End If
        
    Set oXMLDocument = New MSXML2.DOMDocument
    oXMLDocument.loadXML sXML
    If oXMLDocument.parseError.errorCode <> 0 Then
        sXMLReply = "<Error>XML Parse error</Error>"
        lStatus = STATUS_INVALID_REQUEST
        GoTo PrepareReply
    End If
    
    Set oXMLRoot = oXMLDocument.firstChild
    If oXMLRoot Is Nothing Then
        sXMLReply = "<Error>Root node not found</Error>"
        lStatus = STATUS_INVALID_REQUEST
        GoTo PrepareReply
    End If

If the XML request is valid, XMLRequest looks for the <Request> node and for the <ProjectRename> child node. If the <ProjectRename> node is present, XMLRequest uses the Project Server Security object (m_oPjSvrSecurity) to check the permissions before renaming any projects.

    Dim sNodeName As String
    sNodeName = oXMLRoot.nodeName
    Select Case sNodeName

        Case "Request"
            Dim oNode As MSXML2.IXMLDOMNode
            Dim oChildNode As MSXML2.IXMLDOMNode
                
            For Each oNode In oXMLRoot.childNodes
                sNodeName = oNode.nodeName
                Select Case sNodeName
                    Case "ProjectRename"
                        'Check appropriate global user permissions for this call (if necessary)
                        If m_oPjSvrSecurity.CheckSPGlobalPermission(m_sUserGUID, _
                                PERMISSION_ADMIN_ENTERPRISE) <> 0 _
                            And m_oPjSvrSecurity.CheckSPGlobalPermission(m_sUserGUID, _
                                PERMISSION_ENT_NEW_PROJECT) <> 0 Then
                            lStatus = RenameProject(oNode)
                            nHandled = 1
                        Else
                            'Return Access Denied if the security check fails
                            lStatus = STATUS_ACCESS_DENIED
                            GoTo PrepareReply
                        End If
                    Case Else
                        lStatus = STATUS_UNKNOWN_REQUEST
                        GoTo PrepareReply
                End Select
            Next oNode
        Case Else
            lStatus = STATUS_UNKNOWN_REQUEST
            GoTo PrepareReply
    End Select
     
    
PrepareReply:
    If lStatus = STATUS_UNKNOWN_REQUEST Then
        nHandled = 0
    Else
        nHandled = 1
    End If
    XMLRequest = "<Reply><HRESULT>0</HRESULT><STATUS>" _
        & Format$(lStatus, "0") & "</STATUS>" & sXMLReply & "</Reply>"

If XMLRequest does not recognize the request, it returns a status code that indicates the error. XMLRequest also sets the nHandled parameter to 0 if it cannot handle the request, or to 1 if it does handle the request. If nHandled is 0, that notifies the PDS to look for another extender that might be able to handle the request.

When the permission checks pass, XMLRequest sends the <ProjectRename> node to the RenameProject function. For more information, see Parsing the ProjectRename Method.