xquery version "0.9-ml"
(:
 : SharePoint Connector - MarkLogic Component API
 :
 : This module provides the simple SMTP-like server semantics.
 :
 : Copyright (c) 2002-2009 Mark Logic Corporation. All Rights Reserved.
 :)
module "http://marklogic.com/sharepoint"

declare namespace mlsp = "http://marklogic.com/sharepoint"
declare namespace xh="http://www.w3.org/1999/xhtml"

default function namespace = "http://www.w3.org/2003/05/xpath-functions"

import module namespace mlsd = "http://marklogic.com/sharepoint/metadata" at "metadata.xqy"
import module namespace mlsu = "http://marklogic.com/sharepoint/uploads" at "uploads.xqy"

define variable $mlsp:sharepoint-connector-version { "1.0-2" }

(: These values must be synchronized with those in the C# module MLConnector enum Command :)
define variable $mlsp:COMMAND-NOOP                  { 0 }
define variable $mlsp:COMMAND-STATUS                { 1 }
define variable $mlsp:COMMAND-GET-METADATA          { 2 }
define variable $mlsp:COMMAND-PUT-METADATA          { 3 }
define variable $mlsp:COMMAND-COPY-DOC              { 4 }   (: Obsolete :)
define variable $mlsp:COMMAND-DELETE-DOC            { 5 }
define variable $mlsp:COMMAND-GET-SITES             { 6 }
define variable $mlsp:COMMAND-GET-LISTS             { 7 }
define variable $mlsp:COMMAND-GET-FOLDERS           { 8 }
define variable $mlsp:COMMAND-GET-DOCS              { 9 }
define variable $mlsp:COMMAND-GET-DOC-PROPS         { 10 }
define variable $mlsp:COMMAND-GET-URI-LIST          { 11 }

define variable $mlsp:COMMAND-ITEM-EVENT-ADDED      { 100 }
define variable $mlsp:COMMAND-ITEM-CHECKED-OUT      { 101 } (: Unused :)
define variable $mlsp:COMMAND-ITEM-CHECKED-IN       { 102 }
define variable $mlsp:COMMAND-ITEM-DELETED          { 103 }
define variable $mlsp:COMMAND-ITEM-MOVED            { 104 }
define variable $mlsp:COMMAND-ITEM-UNCHECKED-OUT    { 105 } (: Unused :)
define variable $mlsp:COMMAND-ITEM-UPDATED          { 106 }
define variable $mlsp:COMMAND-ITEM-ATTACHMENT-ADDED { 107 } (: Unused :)
define variable $mlsp:COMMAND-ITEM-ATTACHMENT-DELETED{ 108 }(: Unused :)
define variable $mlsp:COMMAND-ITEM-CONVERTED        { 109 } (: Unused :)

define variable $mlsp:COMMAND-RESET                 { 200 }

(: Return codes must be synchronized with those in the C# module MLConnector enum Code :)
define variable $mlsp:CODE-OK                 { 200 }
define variable $mlsp:CODE-RESOURCE-CREATED   { 201 }
define variable $mlsp:CODE-SEND-CONTENT       { 210 }
define variable $mlsp:CODE-SEND-METADATA      { 211 }
define variable $mlsp:CODE-REQUEST-IGNORED    { 212 }
define variable $mlsp:CODE-UNRECOGNIZED-COMMAND{ 400 }
define variable $mlsp:CODE-PATH-NOT-FOUND     { 404 }
define variable $mlsp:CODE-NOT-ACCEPTABLE     { 406 }
define variable $mlsp:CODE-SERVER-ERROR       { 500 }

(: Document mode values on objects, i.e., doc libraries, folders, and documents :)
define variable $mlsp:MODE-DONT-COPY          { 0 } (: Document is not copied :)
define variable $mlsp:MODE-COPY               { 1 } (: SharePoint events may not update the document :)
define variable $mlsp:MODE-MIRROR             { 2 } (: SharePoint events may update the document :)

(:
 : Action functions
 :)

(: Get some info from the server :)
define function mlsp:mlc-status-request(
) as xs:string*
{
    (
      xs:string($mlsp:CODE-OK), 
      "nvp",
      concat("SPC-version=", $mlsp:sharepoint-connector-version),
      concat("ML-version=", xdmp:version()),
      concat("ML-edition=", xdmp:product-edition()),
      concat("ML-platform=", xdmp:platform()), 
      concat("ML-server=", xdmp:server-name(xdmp:server())),
      concat("ML-time=", current-date(), '/', current-time()),
      concat("sites=", count(mlsd:get-sites()))
    )
}

(: Interrogate checks to see if documents or folders exist on ML server :)
define function mlsp:mlc-get-metadata-request(
  $path as xs:string,     (: SharePoint document path :)
  $meta as element()*         (: SharePoint document metadata :)
) as xs:string*
{
  if ($path ne mlsd:doc-path($meta)) then
    let $site := mlsd:get-site-from-path($path)
    return
      if ($path eq $site) then
        let $props := mlsd:get-dir-props($path)
        return
          if (exists($props)) then
          (
            xs:string($mlsp:CODE-OK),
            "nvp",
            mlsd:metadata-to-nvp($props) 
          )
          else
          (
            xs:string($mlsp:CODE-SEND-METADATA),
            "Site properties not found; send metadata"
          )
      else
      (
        xs:string($mlsp:CODE-NOT-ACCEPTABLE),
        "Path does not match metadata; nor is it a site"  
      )
  
  else if (string(mlsd:props-get-type($meta)) eq "doc") then
    let $dp := mlsd:doc-path($meta)
    let $dpdoc := doc($dp)
    let $props := mlsd:get-doc-props($dp)
    return
      if (not(exists($dpdoc))) then       (: No document:)
      (
        xs:string($mlsp:CODE-SEND-CONTENT),
        concat("list 1 ", "Document not found; send content"),
        xdmp:uri-format($dp)  
      )
      else if (not(exists($props))) then  (: No properties:)
      (
        xs:string($mlsp:CODE-SEND-METADATA),
        "Document found; send metadata"  
      )
      else if (mlsd:is-more-recent($meta, $props)) then (: Metadata more recent than properties :)
        if (mlsd:hash-changed($meta, $props)) then
        (
          xs:string($mlsp:CODE-SEND-CONTENT),
          concat("list 1 ", "Document out of date; send content"),
          xdmp:uri-format($dp)  
        )
        else
        (
          xs:string($mlsp:CODE-SEND-METADATA),
          "Document properties out of date; send metadata"
        )
      else  (: Everything is up-to-date :)
      (
        xs:string($mlsp:CODE-OK),
        "nvp",
        mlsd:metadata-to-nvp($props) 
      )
      
  else
    let $dp := mlsd:add-trailing-slash(mlsd:doc-path($meta)) 
    let $dpprops := xdmp:document-properties($dp)/prop:properties
    let $props := mlsd:get-dir-props($dp)
    return
      if (not(exists($dpprops))) then       (: No directory :)
      (
        xs:string($mlsp:CODE-PATH-NOT-FOUND),
        "Directory not found"  
      )
      else if (not(exists($props))) then  (: No properties :)
      (
        xs:string($mlsp:CODE-SEND-METADATA),
        "Directory found with no properties"  
      )
      else  (: Everything is up-to-date; folders don't have a lastModified property on SP :)
      (
        xs:string($mlsp:CODE-OK),
        "nvp",  
        mlsd:metadata-to-nvp($props) 
      )
}

(: Set SharePoint metadata for a document. :)
define function mlsp:mlc-put-metadata-request(
  $path as xs:string,     (: SharePoint document path :)
  $meta as element()*     (: SharePoint document metadata :)
) as xs:string*
{
  if ($path ne mlsd:doc-path($meta)) then
  (
    xs:string($mlsp:CODE-NOT-ACCEPTABLE),
    "Path does not match metadata"  
  )
  
  else if (string(mlsd:props-get-type($meta)) eq "doc") then
    let $dp := mlsd:doc-path($meta)
    let $dpdoc := doc($dp)
    let $props := mlsd:get-doc-props($dp)
    return
      if (not(exists($dpdoc))) then       (: No document--error :)
      (
        xs:string($mlsp:CODE-PATH-NOT-FOUND),
        "Document not found"  
      )
      else if (not(exists($props))) then  (: No properties--add them :)
      (
        xdmp:document-add-properties($dp, $meta),
        mlsu:uploads-unset($path),
        
        xs:string($mlsp:CODE-OK),
        "Metadata added to document"  
      )
      else if (mlsd:is-more-recent($meta, $props)) then (: Metadata more recent than properties :)
        if (mlsd:hash-changed($meta, $props)) then
        (
          xdmp:document-set-properties($dp, $meta),(: Replace the props to prevent multiple SEND-CONTENT responses :)
          mlsu:uploads-set($path),
        
          xs:string($mlsp:CODE-SEND-CONTENT),
          concat("list 1 ", "Document properties replaced; send content"),
          xdmp:uri-format($dp)  
        )
        else
        (
          xdmp:document-set-properties($dp, $meta),(: Replace the props :)
          mlsu:uploads-unset($path),
        
          xs:string($mlsp:CODE-OK),
          "Document properties replaced; content up to date"
        )
      else  (: Everything is up-to-date :)
      (
        mlsu:uploads-unset($path),
        
        xs:string($mlsp:CODE-OK),
        "Document up to date"  
      )
      
  else
    let $dp := mlsd:add-trailing-slash(mlsd:doc-path($meta)) 
    let $dpprops := xdmp:document-properties($dp)/prop:properties
    let $props := mlsd:get-dir-props($dp)
    return
      if (not(exists($dpprops))) then       (: No directory--create it and add props :)
      (
        xdmp:directory-create($dp),
        xdmp:document-add-properties($dp, $meta),
        
        xs:string($mlsp:CODE-OK),
        "Directory created; metadata added"  
      )
      else if (not(exists($props))) then  (: No properties--add them :)
      (
        xdmp:document-add-properties($dp, $meta),
        
        xs:string($mlsp:CODE-OK),
        "Metadata added to directory"  
      )
      else  (: Everything is up-to-date; folders don't have a lastModified property on SP :)
      (
        xdmp:document-set-properties($dp, $meta),(: Update folder props now since there is no content :)
              
        xs:string($mlsp:CODE-OK),
        "Directory metadata replaced"  
      )
}

(: Delete the MarkLogic copy of a SharePoint document :)
define function mlsp:mlc-delete-request(
  $path as xs:string      (: SharePoint document path :)
) as xs:string*
{
  let $dpath := mlsd:add-trailing-slash($path) (: Add trailing slash to a presumptive directory :)
  let $dpprops := mlsd:get-dir-props($dpath)
  return
    if (exists(doc($path))) then          (: Found a document :)
    (
      xdmp:document-delete($path),
      
      xs:string($mlsp:CODE-OK), 
      "Document deleted"
    )
    else if (exists($dpprops)) then  (: Found a directory :)
    (
      xdmp:directory-delete($dpath),
      
      xs:string($mlsp:CODE-OK), 
      "Directory deleted"
    )
    else
    (
      xs:string($mlsp:CODE-PATH-NOT-FOUND),
      "Path not found"  
    )
}

(: Scan the metadata and return the SharePoint site URLs that have been copied :)
define function mlsp:mlc-get-sites-request(
) as xs:string*
{
  let $list := mlsd:get-sites()
  return
    (
      xs:string($mlsp:CODE-OK), 
      concat("list ", count($list)),
      $list
    )
}

(: Scan the metadata for a particular SharePoint site URL and return the SharePoint document libraries copied :)
define function mlsp:mlc-get-lists-request(
  $path as xs:string      (: SharePoint site URL :)
) as xs:string*
{
  let $site := mlsd:get-sites()[. = $path]
  return
    if (empty($site)) then
    (
      xs:string($mlsp:CODE-PATH-NOT-FOUND), 
      "Site not found"
    )
    else
      (: Document libraries have no parentFolder :)
      let $list := xdmp:document-properties()[./property::mlsp:webUrl eq $site
          and ./property::mlsp:parentFolder eq ""]/property::mlsp:uri
       return
        (
          xs:string($mlsp:CODE-OK), 
          concat("list ", count($list)),
          for $e in $list
          let $u := string($e)
          order by $u
          return
            $u
        )
}

(: Scan the metadata for the SharePoint site 
 : and return the list of folders from that site copied to MarkLogic
 :)
define function mlsp:mlc-get-folders-request(
  $path as xs:string      (: SharePoint doc URL :)
) as xs:string*
{
  let $site := mlsd:get-sites()[. eq $path]
  return
    if (empty($site)) then
    (
      xs:string($mlsp:CODE-PATH-NOT-FOUND), 
      "Site not found"
    )
    else
      let $list := xdmp:document-properties()[./property::mlsp:webUrl eq $site 
          and ./property::mlsp:type eq "folder" 
          and starts-with(./property::mlsp:uri, $path)]/property::mlsp:uri
      return
        (
          xs:string($mlsp:CODE-OK), 
          concat("list ", count($list)),
          for $e in $list
          let $u := string($e)
          order by $u
          return
            $u
            
        )
}

(: Scan the metadata for the SharePoint site specified in the metadata or inferred from the path,
 : and return the list of documents from that site copied to MarkLogic
 :)
define function mlsp:mlc-get-docs-request(
  $path as xs:string      (: SharePoint doc URL prefix :)
) as xs:string*
{
  let $site := mlsd:get-site-from-path($path)
  return
    if (empty($site)) then
    (
      xs:string($mlsp:CODE-PATH-NOT-FOUND), 
      "Site not found"
    )
    else
      let $list := 
          for $m in xdmp:document-properties()[./property::mlsp:webUrl eq $site 
              and ./property::mlsp:type eq "doc" 
              and starts-with(./property::mlsp:uri, $path)]/property::mlsp:uri
          let $u := string($m)
          order by $u
          return
            $u
             
      return
        (
          xs:string($mlsp:CODE-OK), 
          concat("list ", count($list)),
          $list
        )
}

(: Return a NVP list of all URIs, folders and documents, with the value
 : indicating the type and copy mode
 :)
define function mlsp:mlc-get-uri-list-request(
  $path as xs:string      (: SharePoint doc URL :)
) as xs:string*
{
  let $site := mlsd:get-site-from-path($path)
  return
    if (empty($site)) then
    (
      xs:string($mlsp:CODE-PATH-NOT-FOUND), 
      "Site not found"
    )
    else
      let $all := xdmp:document-properties()[./property::mlsp:webUrl eq $site 
          and starts-with(./property::mlsp:uri, $path)]/property::mlsp:uri
      return
        (
          xs:string($mlsp:CODE-OK), 
          "nvp",
          for $x in $all
          let $u := string($x)
          let $p := mlsd:get-props($u)
          let $type := string(mlsd:props-get-type($p))
          let $mode := string(mlsd:props-get-mode($p))
          order by $u
          return
            concat($u, "=", "type:", $type, "|", "mode:", $mode)
    )
}

(: Get the metadata (in a list form) of a document stored on MarkLogic :)
define function mlsp:mlc-get-doc-props-request(
  $path as xs:string      (: SharePoint doc URL :)
) as xs:string*
{
  let $d := doc($path)
  let $props := mlsd:get-doc-props($path)
  let $dprops := mlsd:get-dir-props($path)
  return
    if (exists($d) and exists($props)) then
    (
      xs:string($mlsp:CODE-OK), 
      "nvp",
      mlsd:metadata-to-nvp($props)
    )
    else if (exists($dprops)) then
    (
      xs:string($mlsp:CODE-OK), 
      "nvp",
      mlsd:metadata-to-nvp($dprops)
    )
    else
    (
      xs:string($mlsp:CODE-PATH-NOT-FOUND), 
      "Document or metadata not found"
    )
}

(: Handle the SharePoint itemAdded event - This event is signaled when a document is added 
 : to a document library due to a new, upload, copy, etc.
 :)
define function mlsp:mlc-item-added-request(
  $path as xs:string,     (: SharePoint document path :)
  $meta as element()*     (: SharePoint document metadata (should have mode set to MIRROR) :)
) as xs:string*
{
  if ($path ne mlsd:doc-path($meta)) then
  (
    xs:string($mlsp:CODE-NOT-ACCEPTABLE),
    "Path does not match metadata"  
  )
  
  else
    (: Get controlling folder info to see if we need to respond to this event :)
    let $cf := mlsd:get-controlling-folder($meta)
    let $cfprops := mlsd:get-dir-props($cf)
    return
      if (not(exists($cf)) or not(exists($cfprops))) then
      (
        xs:string($mlsp:CODE-REQUEST-IGNORED), 
        "No controlling directory"
      )
      else if (mlsd:props-get-mode($cfprops) ne $mlsp:MODE-MIRROR) then
      (
        xs:string($mlsp:CODE-REQUEST-IGNORED), 
        "Not mirrored"
      )
      else
        (: Get the target document/folder info :)
        let $props := mlsd:get-props($path)
        let $dpath := mlsd:add-trailing-slash($path)  (: Add trailing slash to a presumptive directory :)
        return
          if (mlsd:props-get-type($meta) eq "folder") then
            if (exists($props)) then
            (
              xdmp:document-set-properties($dpath, $meta),(: Update folder props now since there is no content :)
              
              xs:string($mlsp:CODE-OK), 
              "Directory properties replaced"
            )
            else
            (
              xdmp:directory-create($dpath),        (: Create folder proprs now since there is no content :)
              xdmp:document-set-properties($dpath, $meta),
              
              xs:string($mlsp:CODE-OK),
              "Directory created"  
            )
          else if (not(mlsu:uploads-is-pending($path))) then
          (
            mlsu:uploads-set($path),
            
            xs:string($mlsp:CODE-SEND-CONTENT),     (: Let client send content and properties :)
            concat("list 1 ", "Send content"),
            xdmp:uri-format($path)  
          )
          else
          (
            xs:string($mlsp:CODE-OK), 
            "Upload in progress"
          )
}

(: Handle the SharePoint itemCheckedIn event - This event is signaled when a document is checked-in.
 : Should not apply to folders.
 :)
define function mlsp:mlc-item-checkedin-request(
  $path as xs:string,     (: SharePoint document path :)
  $meta as element()*     (: SharePoint document metadata (should have mode set to MIRROR) :)
) as xs:string*
{
  if ($path ne mlsd:doc-path($meta)) then
  (
    xs:string($mlsp:CODE-NOT-ACCEPTABLE),
    "Path does not match metadata"  
  )
  else if (mlsd:props-get-type($meta) ne "doc") then
  (
    xs:string($mlsp:CODE-NOT-ACCEPTABLE),
    "Can only check-in documents"  
  )
  
  else
    let $cf := mlsd:get-controlling-folder($meta)
    let $cfprops := mlsd:get-dir-props($cf) 
    return
      if (not(exists($cf)) or not(exists($cfprops))) then
      (
        xs:string($mlsp:CODE-REQUEST-IGNORED), 
        "No controlling directory"
      )
      else if (mlsd:props-get-mode($cfprops) ne $mlsp:MODE-MIRROR) then
      (
        xs:string($mlsp:CODE-REQUEST-IGNORED), 
        "Not mirrored"
      )
      else
        let $props := mlsd:get-doc-props($path)
        return
          if (exists($props)) then
            if (mlsd:is-more-recent($meta, $props)) then
              if (mlsd:hash-changed($meta, $props)) then
              (
                xdmp:document-set-properties($path, $meta),      (: Replace the props to prevent multiple SEND-CONTENT responses :)
              
                xs:string($mlsp:CODE-SEND-CONTENT),
                concat("list 1 ", "Document properties replaced; send content"),
                xdmp:uri-format($path)  
              )
              else
              (
                xdmp:document-set-properties($path, $meta),      (: Replace the props :)
              
                xs:string($mlsp:CODE-OK),
                "Document properties replaced; content up to date"
              )
            else
            (
              xs:string($mlsp:CODE-OK),
              "Document up to date"
            )
          else if (not(mlsu:uploads-is-pending($path))) then
          (
            mlsu:uploads-set($path),
            
            xs:string($mlsp:CODE-SEND-CONTENT),     (: Let client send content and properties :)
            concat("list 1 ", "Send content"),
            xdmp:uri-format($path)  
          )
          else
          (
            xs:string($mlsp:CODE-OK), 
            "Upload in progress"
          )
          
}

(: Handle the SharePoint itemDeleted event - This event is signaled when a document is deleted
 :)
define function mlsp:mlc-item-deleted-request(
  $path as xs:string,     (: SharePoint document path :)
  $meta as element()*     (: SharePoint document metadata :)
) as xs:string*
{
  if ($path ne mlsd:doc-path($meta)) then
  (
    xs:string($mlsp:CODE-NOT-ACCEPTABLE),
    "Path does not match metadata"  
  )
  
  else
    let $cf := mlsd:get-controlling-folder($meta)
    let $cfprops := mlsd:get-dir-props($cf)
    return
      if (not(exists($cf)) or not(exists($cfprops))) then
      (
        xs:string($mlsp:CODE-REQUEST-IGNORED), 
        "No controlling directory"
      )
      else if (mlsd:props-get-mode($cfprops) ne $mlsp:MODE-MIRROR) then
      (
        xs:string($mlsp:CODE-REQUEST-IGNORED), 
        "Not mirrored"
      )
      else    
        if (mlsd:props-get-type($meta) eq "folder") then
          let $props := mlsd:get-dir-props($path)
          let $dpath := mlsd:add-trailing-slash($path)  (: Add trailing slash to a presumptive directory :)
          let $dpprops := xdmp:document-properties($dpath)/prop:properties
          return
            if (exists($props)) then
              if (mlsd:props-get-mode($props) ne $mlsp:MODE-MIRROR) then
              (
                (: Mirroring implies coupling with SharePoint events; not mirroring implies no coupling :)
                xs:string($mlsp:CODE-REQUEST-IGNORED), 
                "Directory not mirrored"
              )
              else
              (
                xdmp:directory-delete($dpath),
                
                xs:string($mlsp:CODE-OK), 
                "Directory deleted"
              )
            else if (exists($dpprops)) then
            (
              xdmp:directory-delete($dpath),
              
              xs:string($mlsp:CODE-OK), 
              "Directory deleted; no properties"
            )
            else
            (
              xs:string($mlsp:CODE-REQUEST-IGNORED), 
              "Directory not found"
            )
            
        else (: mlsd:props-get-type($meta) eq "doc") :)
          let $props := mlsd:get-doc-props($path)
          return
            if (exists($props)) then
              if (mlsd:props-get-mode($props) ne $mlsp:MODE-MIRROR) then
              (
                xs:string($mlsp:CODE-REQUEST-IGNORED), 
                "Document not mirrored"
              )
              else
              (
                xdmp:document-delete($path),
                
                xs:string($mlsp:CODE-OK), 
                "Document deleted"
              )
            else
            (
              xs:string($mlsp:CODE-REQUEST-IGNORED), 
              "Document not found"
            )
}

(: Handle the SharePoint itemMoved event - This event is signaled when a document's uri changes
 :)
define function mlsp:mlc-item-moved-request(
  $path as xs:string,     (: SharePoint document path before the move! :)
  $meta as element()*     (: SharePoint document metadata after the move! :)
) as xs:string*
{
  let $cf := mlsd:get-controlling-folder-from-path($path)
  let $cfprops := mlsd:get-dir-props($cf)
  return
    if (not(exists($cf)) or not(exists($cfprops))) then
    (
      xs:string($mlsp:CODE-REQUEST-IGNORED), 
      "No controlling directory"
    )
    else if (mlsd:props-get-mode($cfprops) ne $mlsp:MODE-MIRROR) then
    (
      xs:string($mlsp:CODE-REQUEST-IGNORED), 
      "Not mirrored"
    )
    else    
      if (mlsd:props-get-type($meta) eq "folder") then
        let $dpath := mlsd:add-trailing-slash($path)
        let $props := mlsd:get-dir-props($path)
        let $rawprops := xdmp:document-properties($dpath) (: in case this directory was never explicitly touched :)
        let $newdpath := mlsd:add-trailing-slash(mlsd:doc-path($meta))
        return
          if (exists($props) or exists($rawprops)) then
          (
            mlsd:directory-copy($path, mlsd:doc-path($meta)),
            xdmp:directory-delete($dpath),        (: Delete the directory and all downstream documents :)
            xdmp:document-set-properties($newdpath, $meta),
            
            xs:string($mlsp:CODE-OK), 
            "Directory moved"
          )
          else
          (
            xdmp:directory-create($newdpath),       (: Create new folder :)
            xdmp:document-set-properties($newdpath, $meta),
            
            xs:string($mlsp:CODE-OK), 
            "Directory created"
          )

      else (: (mlsd:props-get-type($meta) eq "doc") :)
        let $dpdoc := doc($path)
        let $dpprops := xdmp:document-properties($path)/prop:properties
        let $mpitem := xdmp:document-properties($path)/prop:properties/item
        return
          if (exists($dpdoc)) then
          (
            mlsd:rename($path, mlsd:doc-path($meta)),           (: Rename the old document :)
            xdmp:document-set-properties(mlsd:doc-path($meta), $meta),
            
            xs:string($mlsp:CODE-OK), 
            "Document moved"
          )
          else if (not(mlsu:uploads-is-pending($path))) then
          (
            mlsu:uploads-set($path),
            
            xs:string($mlsp:CODE-SEND-CONTENT),     (: Let client send content and properties :)
            concat("list 1 ", "Send content"),
            xdmp:uri-format($path)  
          )
          else
          (
            xs:string($mlsp:CODE-OK), 
            "Upload in progress"
          )

}

(: Handle the SharePoint itemUpdated event - This event is signaled when a document metadata is changed,
 : and just prior to a check-in
 :)
define function mlsp:mlc-item-updated-request(
  $path as xs:string,     (: SharePoint document path :)
  $meta as element()*     (: SharePoint document metadata (should have mode set to MIRROR) :)
) as xs:string*
{
  if ($path ne mlsd:doc-path($meta)) then
  (
    xs:string($mlsp:CODE-NOT-ACCEPTABLE),
    "Path does not match metadata"  
  )
  
  else
    let $cf := mlsd:get-controlling-folder-from-path($path)
    let $cfprops := mlsd:get-dir-props($cf)
    return
      if (not(exists($cf)) or not(exists($cfprops))) then
      (
        xs:string($mlsp:CODE-REQUEST-IGNORED), 
        "No controlling directory"
      )
      else if (mlsd:props-get-mode($cfprops) ne $mlsp:MODE-MIRROR) then
      (
        xs:string($mlsp:CODE-REQUEST-IGNORED), 
        "Not mirrored"
      )
      else    
        let $props := mlsd:get-props($path)
        let $dpath := mlsd:add-trailing-slash($path)  (: Add trailing slash to a presumptive directory :)
        return
          if (mlsd:props-get-type($meta) eq "folder") then
            if (exists($props)) then
            (
              xdmp:document-set-properties($dpath, $meta),
              
              xs:string($mlsp:CODE-OK), 
              "Directory properties replaced"
            )
            else
            (
              xdmp:directory-create($dpath),        (: Create folder proprs now since there is no content :)
              xdmp:document-set-properties($dpath, $meta),
              
              xs:string($mlsp:CODE-OK),
              "Directory created"  
            )
          else if (exists($props)) then
            if (mlsd:is-more-recent($meta, $props)) then
              if (mlsd:hash-changed($meta, $props)) then
              (
                xdmp:document-set-properties($path, $meta),
                mlsu:uploads-set($path),
              
                xs:string($mlsp:CODE-SEND-CONTENT),
                concat("list 1 ", "Document properties replaced; send content"),
                xdmp:uri-format($path)  
              )
              else
              (
                xdmp:document-set-properties($path, $meta),(: Replace the props :)
              
                xs:string($mlsp:CODE-OK),
                "Document properties replaced; content up to date"
              )
            else
            (
              xs:string($mlsp:CODE-OK),
              "Document up to date"
            )
          else if (mlsu:uploads-is-pending($path)) then
          (
            xs:string($mlsp:CODE-OK),
            "Document upload pending"
          )
          else 
          (
            mlsu:uploads-set($path),
            
            xs:string($mlsp:CODE-SEND-CONTENT),
            concat("list 1 ", "Not found; send content"),
            xdmp:uri-format($path)  
          )
}

(: Delete all documents from all sites on MarkLogic server :)
define function mlsp:mlc-reset-request(
) as xs:string*
{
  let $step :=
      (: Truncate the site URIs to their hostnames to avoid conflicting deletes :)
      let $list := distinct-values(
          for $x in distinct-values(mlsd:get-sites())
          let $y := replace($x, "http://([^/]*)/.*", "http://$1")
          return 
            $y
          )
  
      for $site in $list
      return
        xdmp:directory-delete(concat($site, "/"))   
  return
    (
      xs:string($mlsp:CODE-OK), 
      "MarkLogic server reset"
    )
}

(:
 : Main request function
 :)
 
define function mlsp:mlc-request(
  $command as xs:integer, (: Command code :)
  $path as xs:string,     (: SharePoint document path :)
  $meta as element()*     (: SharePoint document metadata :)
) as xs:string*
{
  if ($command eq $mlsp:COMMAND-NOOP) then
  (
    xdmp:trace("SPC perf", $path),
  
    xs:string($mlsp:CODE-OK),
    "MarkLogic Server/SharePoint Connector"
  )
    
  else if ($command eq $mlsp:COMMAND-STATUS) then
    mlsp:mlc-status-request()
  
  else if ($command eq $mlsp:COMMAND-GET-METADATA) then
    mlsp:mlc-get-metadata-request($path, $meta)
  
  else if ($command eq $mlsp:COMMAND-PUT-METADATA) then
    mlsp:mlc-put-metadata-request($path, $meta)
  
  else if ($command eq $mlsp:COMMAND-DELETE-DOC) then
    mlsp:mlc-delete-request($path)
  
  else if ($command eq $mlsp:COMMAND-GET-SITES) then
    mlsp:mlc-get-sites-request()
  
  else if ($command eq $mlsp:COMMAND-GET-LISTS) then
    mlsp:mlc-get-lists-request($path)
    
  else if ($command eq $mlsp:COMMAND-GET-FOLDERS) then
    mlsp:mlc-get-folders-request($path)
    
  else if ($command eq $mlsp:COMMAND-GET-DOCS) then
    mlsp:mlc-get-docs-request($path)
    
  else if ($command eq $mlsp:COMMAND-GET-DOC-PROPS) then
    mlsp:mlc-get-doc-props-request($path)
    
  else if ($command eq $mlsp:COMMAND-GET-URI-LIST) then
    mlsp:mlc-get-uri-list-request($path)
    
  else if ($command eq $mlsp:COMMAND-ITEM-EVENT-ADDED) then
    mlsp:mlc-item-added-request($path, $meta)
    
  else if ($command eq $mlsp:COMMAND-ITEM-CHECKED-IN) then
    mlsp:mlc-item-checkedin-request($path, $meta)
    
  else if ($command eq $mlsp:COMMAND-ITEM-DELETED) then
    mlsp:mlc-item-deleted-request($path, $meta)
    
  else if ($command eq $mlsp:COMMAND-ITEM-MOVED) then
    mlsp:mlc-item-moved-request($path, $meta)
    
  else if ($command eq $mlsp:COMMAND-ITEM-UPDATED) then
    mlsp:mlc-item-updated-request($path, $meta)
    
  else if ($command eq $mlsp:COMMAND-RESET) then
    mlsp:mlc-reset-request()
    
  else
    (
      xs:string($mlsp:CODE-UNRECOGNIZED-COMMAND), 
      "Unimplemented command"
    )
    
}
