Resolution of topicref starting with keydef in keyspace appears to work

(: =====================================================

   DITA Link Management Utilities

   Utilities that support general DITA link management
   actions.

   Author: W. Eliot Kimber

   Copyright (c) 2015 DITA For Small Teams
   Licensed under Apache License 2

   ===================================================== :)

module namespace lmutil="http://dita-for-small-teams.org/xquery/modules/linkmgmt-utils";

declare namespace dfst="http://dita-for-small-teams.org";

import module namespace df="http://dita-for-small-teams.org/xquery/modules/dita-utils";
import module namespace bxutil="http://dita-for-small-teams.org/xquery/modules/basex-utils";
import module namespace dfstcnst="http://dita-for-small-teams.org/xquery/modules/dfst-constants";
import module namespace relpath="http://dita-for-small-teams.org/xquery/modules/relpath-utils";

declare variable $lmutil:useMatchParamsLocalMaps :=
                               map{'linktype' :'topicref',
                                   'format' : 'ditamap',
                                   'scope' : 'local'
                                  };
declare variable $lmutil:useMatchParamsPeerMaps :=
                               map{'linktype' :'topicref',
                                   'format' : 'ditamap',
                                   'scope' : 'peer'
                                  };

(:~
 : Find all links that use direct URI references to their target resources.
 : Returns list of link item maps
 :
 : Each link item map has the following items:
 :
 : 'link' : The element that is the link
 : 'rootMap' : The root map document that defines the keyspace the link
 :             is resolved in.
 : 'resolvedMap' : The fully-resolved map that defines key space the key
 :                 reference is resolved in.
 : 'keySpace' : The constructed key space for the root map.
 : 'processingRole' : For topicrefs, the @processing-role value. For
 :                    other links, 'normal'.
 : 'linkContext': The relevant container type for the link, one of:
 :                - 'title' : Link occurs within a title element
 :                - 'prolog' : Link occurs with topicmeta (maps) or prolog (topics)
 :                - 'shortdesc' : Link occurs within a short description
 :                - 'abstract' : Link occurs within an abstract (but not within a
 :                               short description within an abstract)
 :                - 'body' : Link occurs within a topic body
 :                - 'reltable' : Link is a topicref within a relationship table.
 :                - 'navtree' : Link is a topicref with @processing-role of 'normal' and
 :                              is not within a relationship table
 :                - 'resources' : Link is a topicref with a processing role of "resource-only".
 :  'isDirect' : - 'true' if link is a direct URI reference
 :               - 'false' if link uses a resolvable key reference
 :
 : For direct links the link element is sufficient
 : to allow resolution as use context has no affect on how the link is resolved.
 : For "." ("this topic") fragment IDs, the context is always the containing topic.
 :
 : @param dbName Name of the database that contains the documents to be processed.
 : @param logID ID of the log to put log messages in.
 :)
declare function lmutil:findAllDirectLinks($dbName) as map(*)* {
  let $db := db:open($dbName)
  (: First do direct URI references, which don't require any
     context knowledge:
   :)
  let $links := collection($dbName)//*[df:isTopicRef(.) and not(@keyref)] |
                collection($dbName)//*[contains(@class, ' topic/xref ') and (@href and not(@keyref))] |
                collection($dbName)//*[contains(@class, ' topic/data-about ') and (@href and not(@keyref))] |
                collection($dbName)//*[contains(@class, ' topic/longdescref ') and (@href and not(@keyref))] |
                collection($dbName)//*[@conref]
   (: Construct a link item map for each link element :)
   for $link in $links
       return lmutil:constructLinkItemMap($link)
};

(:~
 : Constructs a link item map for a direct link.
 :
 : @param link The link element
 : @return Link item map
 :)
declare function lmutil:constructLinkItemMap(
                        $link as element()) as map(*) {
  let $contentDocURI := document-uri(root($link))
  let $result := lmutil:constructLinkItemMap($link, $contentDocURI, ())
  return $result
};

(:~
 : Constructs a link item map
 :
 : @param link The link element
 : @param topicref For indirect links, the topicref that establishes
 :                 the key resolution context for the link. Not set for direct URI links.
 :                 Note that this topicref element must be from the resolved map, not the
 :                 original source map.
 :)
declare function lmutil:constructLinkItemMap(
                        $link as element(),
                        $contentDocURI as xs:string,
                        $topicref as element()?) as map(*) {
   let $linkItem :=
      map{'link': $link,
          'resolvedMap': if ($topicref) then root($topicref)/* else (),
          'topicref' : $topicref,
          'contentDocURI' : $contentDocURI,
          'processingRole':
             if (df:isTopicRef($link))
                then if ($link/@processing-role)
                        then string($link/@processing-role)
                        else 'normal' (: Default for topicref :)
                else 'normal', (: non-topicref links :)
          'linkContext': lmutil:getLinkContext($link)
         }
    return $linkItem
};

(: Give a document, finds all references to that document that match the
   type of uses as configured in the $useParams.

   @param doc Document to find uses of.
   @param useParams Use filter parameters. Only uses that match the parameters
                    will be reported.
   @return Zero or more use record elements.

   The use parameters are:

   linktype: List of base type names (e.g., 'topicref, xref) or qualified
             class names (e.g., 'map-d/navref') of the types of links
             to report usage for. If unspecifed, all link types are reported.
             The keyword "#conref" indicates uses via @conref or @conkeyref

   format:   List of @format values by which the document is used, e.g. "dita", "ditamap",
             etc. If unspecified, uses are not filtered by @format value.

   scope:    List of @scope values to filter the uses by. If unspecified, uses are not
             filtered by @scope value.

   TBD: Need for direct/indirect filter, other filters.

   FIXME: Need to rationalize the coding pattern for use parameters and
          their use by the useRecordMatcher() function. The code is a little
          confused right now as a result of some quick refactoring.

 :)
declare function lmutil:getUses($elem as element(), $useParams) as element()* {

   let $linktypes := if ($useParams('linktype'))
                        then $useParams('linktype')
                        else ('#any')
   let $formats   := if ($useParams('format'))
                        then $useParams('format')
                        else ('#any')
   let $scopes   := if ($useParams('scope'))
                        then $useParams('scope')
                        else ('#any')

   (: Note that all references are ultimately to elements,
      so resource keys are always for elements, not
      documents. Given an element we can always get its
      containing document.

      In DITA, except for <dita> documents, references to
      documents with no fragment identifier are implicitly
      to the root elements of those documents (i.e., a map
      or topic element).

      The resource key here is used as the name of the
      directory that contains the where-used records for
      this element.
    :)
   let $resKey := lmutil:constructResourceKeyForElement($elem)

   (: Now find all use records for the resource key that match the filter
      specification.

      :)
    let $dbName := bxutil:getMetadataDbNameForDoc(root($elem))
    let $collection := $dbName || $dfstcnst:where-used-dir || '/' || $resKey
    let $records := collection($collection)
                       /dfst:useRecord[lmutil:useRecordMatcher(., $linktypes, $formats, $scopes)]
    return $records

};

(: Given an element, construct the unique resource key for it.
   For a given element the resource ID is guaranteed to be unique within
   a snapshot (git commit).

   @param elem Element to get resource ID for.
   @returns Resource key string.

   The resource key is a combination of the absolute URI of the containing
   document and the element's tree position within the document, producing
   a unique key for any element in any document on a given snapshot (version
   in time). Resource keys are not reliably unique across snapshots as the position of
   the element within its containing document could change from version to
   version.

   The resource key is used to look up the element in where-used records, keyspaces,
   and other link management metadata indexes, either
   as the element used or the element doing the use (links).

 :)
declare function lmutil:constructResourceKeyForElement($elem as element()) as xs:string {
   lmutil:constructResourceKeyForElement(document-uri(root($elem)), $elem)
};

(: Given an element, construct the unique resource key for it.
   For a given element the resource ID is guaranteed to be unique within
   a snapshot (git commit).

   @param docURI URI of the document that contains the element.
   @param elem Element to get resource ID for.
   @returns Resource key string.

   The resource key is a combination of the absolute URI of the containing
   document and the element's tree position within the document, producing
   a unique key for any element in any document on a given snapshot (version
   in time). Resource keys are not reliably unique across snapshots as the position of
   the element within its containing document could change from version to
   version.

   The resource key is used to look up the element in where-used records, either
   as the element used or the element doing the use (links).

 :)
declare function lmutil:constructResourceKeyForElement(
                                        $docURI as xs:string,
                                        $elem as element()) as xs:string {
  let $targetDocHash := replace(string(hash:md5($docURI)), '/', '~')
  let $treepos := for $anc in ($elem/ancestor-or-self::*)
                      return string(count($anc | $anc/preceding-sibling::*))
  let $key := string-join($treepos, '.')
  return $targetDocHash || '^' || $key

};

(:~
 : Determines if a given where-used record matches the filter
 : specified parameters.
 :
 : FIXME: Use a map to pass the filter parameters rather than
 : individual arguments.
 :)
declare function lmutil:useRecordMatcher($record as element(),
                                         $linktypes as xs:string*,
                                         $formats as xs:string*,
                                         $scopes as xs:string*) as xs:boolean {
   let $result := ((if ($linktypes = '#any')
                       then true()
                       else string($record/@linkType) = $linktypes) and
                   (if ($formats = '#any')
                       then true()
                       else string($record/@format) = $formats) and
                   (if ($scopes = '#any')
                       then true()
                       else string($record/@scope) = $scopes))
  return $result
};

(:~
 : Given a link element, determines the linkContext value for it.
 :)
declare function lmutil:getLinkContext($link as element()) as xs:string {
  if (df:class($link, 'map/topicref'))
     then if ($link/ancestor::*[df:class(., 'map/reltable')])
             then 'reltable'
          else if ($link/@processing-role = ('resource-only'))
               then 'resources'
          else 'navtree'
     else if ($link/ancestor::*[df:class(., 'topic/title')])
          then 'title'
     else if ($link/ancestor::*[df:class(., 'topic/shortdesc')])
          then 'shortdesc'
     else if ($link/ancestor::*[df:class(., 'topic/abstract')])
          then 'abstract'
     else if ($link/ancestor::*[df:class(., 'topic/prolog') or df:class(., 'map/topicmeta')])
          then 'prolog'
     else if ($link/ancestor::*[df:class(., 'topic/body')])
          then 'body'
     else 'unknown-context'

};

(:~
 : Given a link item map, attempts to resolve the link to an element (in DITA a given link
 : can address at most one element).
 :
 : Returns a map with the following members:
 : 'target': A sequence of zero or more elements addressed
 :           by the link.
 : 'log':    A sequence of zero or more log entry elements
 :           generated by the resolution attempt.
 :)
declare function lmutil:resolveDirectLink($linkItem as map(*)) as map(*) {

   let $link as element()? := $linkItem('link')
   return if ($link)
      then
       let $resultMap := if (df:class($link, 'map/topicref') and ($link/@href))
                            then df:resolveTopicRef($link)
                            else df:resolveNonTopicRefDirectLink($link)

       let $targets := $resultMap('target')
       let $log := (<info>Link: {
                   concat('<',
                          name($link), ' ',
                          lmutil:reportAtts($link, ('href', 'keyref', 'keys', 'conref', 'scope')),
                          '>')} [class: "{string($link/@class)}"], doc: "{document-uri(root($link))}"</info>,
                  if ($targets)
                     then <info>  Link resolved</info>
                     else $resultMap('log')
                  )
       return map{'target' : $targets, 'log' : $log, 'link' : $linkItem}
     else
       map{'target' : (),
                 'log' : (),
                 'link' : $linkItem
                }
};

(: Given a link descriptor map and the database that contains it, attempts
   to resolve the link to an element (in DITA a given link
   can address at most one element).

   Returns a map with the following members:
   'target': A sequence of zero or more elements addressed
             by the link.
   'log':    A sequence of zero or more log entry elements
             generated by the resolution attempt.
   'link':   The input link item.
 :)
declare function lmutil:resolveIndirectLink(
                        $metadataDbName as xs:string,
                        $linkItem as map(*)) as map(*) {

   (: Topicref in resolved map that establishes the use
      context for the linking element. :)
   let $topicref := $linkItem?topicref
   let $link := $linkItem?link
   let $keyName := df:getKeyNameForKeyref($link)
   let $rootMap := $linkItem?rootMap
   let $contentMapURI := string(root($topicref)/*/@origMapURI)

   (: Find the effective key definition for the key name.
      This is a topicref element from a keyspace document.
      :)
   let $keydef := lmutil:findKeyDefinition(
                             $metadataDbName,
                             $keyName,
                             $contentMapURI,
                             $topicref)
   let $targets :=
       if ($keydef)
          then
            let $topicrefInResolvedMap :=
                      lmutil:getResolvedMapTopicrefForKeyspaceTopicref(
                                                         $metadataDbName,
                                                         $keydef)
            return lmutil:resolveTopicRefFromResolvedMap($topicrefInResolvedMap)
          else ()

   let $log := (if (not($keydef))
                   then <warn>No key definition found for key "{$keyName}"</warn>
                   else ()
               )
   return map{'target' : $targets,
              'log' : $log ,
              'link' : $linkItem,
              'keydef' : $keydef
             }
};

(:~
 : Gets the resolved-map topicref that corresponds to a topicref from
 : a keyspace document.
 :)
 declare function lmutil:getResolvedMapTopicrefForKeyspaceTopicref(
                                                 $metadataDbName,
                                                 $keydef) as element() {
    let $resolvedMapURI := string(root($keydef)/*/@resolvedMap)
    let $resID := string($keydef/@resID)
    let $topicrefInResolvedMap := (collection($metadataDbName ||
                        $resolvedMapURI)//*[@resID = $resID])
    return $topicrefInResolvedMap
};

(:~
 : Given a key name and a topicref in a resolved map, find the applicable
 : key-defining topicref, if any.
 :
 : @param keyName The key name to look up.
 : @param topicref The topicref that establishes the map context (and thus
 :                 the starting key scope for the lookup)
 : @return The key-defining topicref or empty sequence if no key definition
 :             is found.
 :)
declare function lmutil:findKeyDefinition(
                            $metadataDbName as xs:string,
                            $keyName as xs:string,
                            $contentMapURI as xs:string,
                            $topicref as element()) as element()? {
  (: Given a topicref, find the key space that it contributes to,
     then find the effective key binding for the key name within
     the key space hierarchy.
   :)
  let $topicrefID := lmutil:constructResourceKeyForElement($contentMapURI, $topicref)
  let $keyDefInKeySpace := collection($metadataDbName ||
    $dfstcnst:keyspaces-dir)//keyspace/keys/key[@name = $keyName]/*[@resID = $topicrefID]

  let $keys := for $keyspace in $keyDefInKeySpace/ancestor::keyspace
                   return $keyspace/keys/key[@name = $keyName]
  let $effectiveKeyDef := $keys[1]/*[1]

  let $result := $effectiveKeyDef
  return $result
};

(:~
 : Given a database, finds all the indirect links. Returns a map
 : the the members.
 : @param dbName Name of the content database to find the links for
 : @return Map with two members:
 :
 : 'links' : A sequence of linkItem maps, where each map represents one link.
 : 'log' : A sequence of log entry elements.
 :
 :
 :)
declare function lmutil:findAllIndirectLinks($dbName) as map(*)* {
   (:

1. Find all root maps (maps with no local-scope topicref references or
   maps with peer topicref references). This requires that the direct-reference
   use records are up to date. It also requires that the resolved map and key space
   documents have already been created.

2. For each root map, Walk the resolved map, creating link items for each key-based topicref.
   Resolve topicrefs to topics and process each topic to create link items for each key-based
   link in each topic.

   :)

   let $rootMaps as element()* := lmutil:getRootMaps($dbName)

   let $linksFromTopics := lmutil:getIndirectLinksFromTopics($rootMaps)

    let $linksFromMaps := () (: FIXME: Build this list as well :)
    let $links := ($linksFromTopics, $linksFromMaps)

   return $links
};

(:~
 : Get all the indirect links in topics referenced from the root maps
 :
 : @para rootMaps list of root content maps.
 :
 : @return Sequence, possibly empty, of link item maps.
 :)
declare function lmutil:getIndirectLinksFromTopics($rootMaps as element()*) as map(*)* {
  let $result :=
     for $map in $rootMaps
         let $resolvedMap as element()? := lmutil:getResolvedMapForMap($map)
         return if ($resolvedMap)
            then
               let $bos as map(*)* := lmutil:getMapBoundedObjectSet($resolvedMap, ('dita'))
               return for $member in $bos
                  return lmutil:getIndirectLinks($bos)
            else ()
  return $result
};

(:~
 : Takes a sequence of map BOS members and, for each member,
 : finds the key-based links in the member and returns a of link
 : structures reflecting those links.
 :
 : @param bos Sequence of maps, one for each use of a topic from a
 : DITA map. Each map has two members, "topicref", and "resource"
 : @return A sequence, possibly empty, of link maps.
 :)
declare function lmutil:getIndirectLinks($bos as map(*)*) as map(*)* {
   let $result as map(*)* :=
       for $member in $bos
           return lmutil:getIndirectLinksForBOSMember($member)
   return $result
};

(:~
 : Given a BOS member map for a topic, finds all key-based links in the
 : topic and returns a sequence of link items for the links.
 :
 : @param member BOS member map containing a topic BOS member.
 : @return A sequence, possibly empty, of link items.
 :)
declare function lmutil:getIndirectLinksForBOSMember($member as map(*)) as map(*)* {
   let $topicref := $member('topicref')
   let $topic := $member('resource')
   let $contentDocURI := "contentdocURI value"
   let $linkElems := $topic//*[@keyref]
   let $linkItems :=
       for $elem in $linkElems
           return lmutil:constructLinkItemMap($elem, $contentDocURI, $topicref)
   return $linkItems
};

(:~
 : Given a map resolves all the topicrefs to their local-scope resources
 : and returns the set of "BOS members", where each member is a map
 : with the topicref that pointed to the topic and the resolved topic.
 : The topicref is required in order to have the map context, which is required
 : in order to determine the key space within which any key references within the
 : topic are resolved. Note that a BOS member is a unique topicref/resource pair.
 : If the same resource is referenced multiple times in the map there will be one BOS member
 : for each reference.
 :
 : @param map Map to process. Does not resolve submap references, so if
 : you want the resources for a map tree supply the resolved map.
 :
 : @param formats A list of one or more @format values to return in the set.
 : The special value '#non-dita' matches any @format value that is not 'dita'
 : or 'dita-map'. Specify ('dita') to return only topic members of
 : the BOS. Non-DITA resources are represented by the topicrefs that point to
 : them. If not specified, all local-scope resources are returned.
 : @return Sequence of maps, one for each topicref/resource pair
 :)
declare function lmutil:getMapBoundedObjectSet(
                            $map as element(),
                            $formats as xs:string*) as map(*)* {
  let $topicrefs := $map//*[df:isTopicRef(.)]
                           [df:isLocalScope(.)]
                           [not($formats) or
                            df:getEffectiveFormat(.) = $formats]
  let $members :=
      for $topicref in $topicrefs
          let $resource := if (df:getEffectiveFormat($topicref) = ('dita'))
             then lmutil:resolveTopicRefFromResolvedMap($topicref)
             else $topicref
          return map { 'topicref' : $topicref,
                       'resource' : $resource }
  return $members

};

(:~
 : Resolves a topicref from a resolved map. Uses the additional metadata
 : in the resolved map to determine the target document that contains
 : the element.
 :)
declare function lmutil:resolveTopicRefFromResolvedMap($topicref as element())
                                                                    as element()? {
  let $baseURI := string(($topicref/ancestor::*[@origMapURI]/@origMapURI)[1])
  let $targetURI := string($topicref/@href)
  let $resolvedURI := relpath:resolveURI($targetURI, $baseURI)
  let $result := try {
    let $doc as document-node()? := doc($resolvedURI)
    let $elem := if ($doc) then $doc/* else()
    return $elem
  } catch * {
    (: Document not found :)
    ()
  }
  return $result
};

(:~
 : Find maps that either have no local-scope topicrefs to them or that have
 : any peer-scope topicrefs to them.
 :
 : @param dbName Name of the content database that holds the map to examine.
 : @return List, possibly empty, of root maps.
 :)
declare function lmutil:getRootMaps($dbName as xs:string) as element()* {
  let $candMaps := collection($dbName)/*[df:class(., 'map/map')]
  let $result :=
      for $candMap in $candMaps
          return if (lmutil:isRootMap($candMap))
                   then $candMap
                   else ()
  return $result
};

(:~
 : Returns true if the specified map element is a root map.
 :
 : Note that there are many ways that root mapness could be determined.
 : The logic used here is any map that has no direct map local-scope references
 : or any peer-scope references. This is based on the presumption that a root
 : map would not normally be useable as local-scope submap. DITA 1.3's new
 : meaning for scope="peer" on topicrefs to maps explicitly means "the referenced
 : map is a root map".
 :
 : Other options would be to have a master map that uses peer map references to
 : explicitly identify root maps and use only that to determine rootness, to
 : maintain some sort of metadata that indicates rootness, use a specific map
 : specialization or map specialization plus descendant element configuration
 : (e.g., bookmaps that have booktitle elements are root maps, all others are
 : not).
 :
 : @param map Map to check for rootness
 : @return true() if the map is a root map.
 :)
declare function lmutil:isRootMap($map as element()) as xs:boolean {
  let $resolvedMap as element()? := lmutil:getResolvedMapForMap($map)
  let $result :=
      if (not($resolvedMap)) (: If no resolved map, link management database hasn't
                                been initialized and have to assume all maps are root:)
         then true()
         else ((lmutil:getRefCount($map, $lmutil:useMatchParamsLocalMaps) = 0) or
               (lmutil:getRefCount($map, $lmutil:useMatchParamsPeerMaps) > 0))
  return $result
};

(:~
 : Gets the number of references to the specified element that match the specified
 : match criteria.
 :
 : @param elem Element to get reference count for.
 : @param matcher Use record matcher to use for selecting references
 : @return Number of matches
 :)
declare function lmutil:getRefCount($elem as element(),
                                    $matchParams as map(*)) as xs:integer {
  let $uses := lmutil:getUses($elem, $matchParams)
  let $result := count($uses)
  return $result
};

(: Construct a string report of the listed attributes :)
declare function lmutil:reportAtts($elem as element(), $attNames) as xs:string {
   let $result := for $att in $elem/@*
                      return if (name($att) = $attNames)
                                then concat(name($att), '="', string($att), '"')
                                else ()
   return string-join($result, ' ')
};

(:~
 : Report a link item map as XML
 :
 : @param linkItem
 : @return XML representation of the link
 :)
declare function lmutil:reportLinkItemMap($linkItem as map(*)) as element() {
(:
 : 'link' : The element that is the link
 : 'rootMap' : The root map document that defines the namespace the link
 :             is resolved in.
 : 'resolvedMap' : The fully-resolved map that defines key space the key
 :                 reference is resolved in.
 : 'keySpace' : The constructed key space for the root map.
 :)
  <linkItem>{
     bxutil:reportMapAsXML($linkItem)
}</linkItem>
};

(:~
 : Get the resolved map document for a map. The resolved map must have already
 : been constructed (XQuery doesn't let use do updating actions wherever we
 : want).
 :
 : @param map Content map to get the resolved map for.
 : @return the resolved map or an empty sequence if there is no resolved map
 : (for example, because the map is not a root map).
 :)
declare function lmutil:getResolvedMapForMap($map as element()) as element()? {
  let $resolvedMapURI as xs:string := lmutil:getResolvedMapURIForMap($map)
  let $metadataDbName as xs:string := bxutil:getMetadataDbNameForDoc(root($map))

  let $resolvedMap := collection($metadataDbName || $resolvedMapURI)
  return $resolvedMap/*
};

(:~
 : Construct the database URI to use for a resolved map.
 :
 : @param map DITA map element to get the URI for
 : @returns The URI of the resolved map as a string
 :)
declare function lmutil:getResolvedMapURIForMap(
                        $map as element()) as xs:string {
  let $mapDocHash := replace(string(hash:md5(document-uri(root($map)))), '/', '~')
  return $dfstcnst:resolved-map-dir ||
         "/" || $mapDocHash || ".ditamap"
};

(:~
 : Given a keyspace element return the document URI to use when
 : storing or finding the key space.
 :
 : @param keyspace Element that is the key space.
 : @return The URI for the key space document.
 :)
declare function lmutil:getKeySpaceURIForKeySpace(
                            $keySpace as element()) as xs:string {
  let $resolvedMapURI as xs:string? := string($keySpace/@resolvedMap)
  let $result := lmutil:getKeySpaceURIForResolvedMapURI($resolvedMapURI)
  return $result
};

(:~
 : Given the URI of a resolved map, construct the URI for the corresponding key space document
 :
 :)
declare function lmutil:getKeySpaceURIForResolvedMapURI($resolvedMapURI as xs:string) as xs:string {
  let $filenameBase := relpath:getNamePart($resolvedMapURI)
  let $uri :=
            let $parentDir := relpath:getParent($resolvedMapURI)
            return relpath:newFile($parentDir,
               concat($filenameBase, '.keyspace'))
  return $uri
};

(: End of Module :)

(: =====================================================

   DITA Link Management Utilities

   Utilities that support general DITA link management
   actions.

   Author: W. Eliot Kimber

   Copyright (c) 2015 DITA For Small Teams
   Licensed under Apache License 2

   ===================================================== :)

module namespace lmutil="http://dita-for-small-teams.org/xquery/modules/linkmgmt-utils";

declare namespace dfst="http://dita-for-small-teams.org";

import module namespace df="http://dita-for-small-teams.org/xquery/modules/dita-utils";
import module namespace bxutil="http://dita-for-small-teams.org/xquery/modules/basex-utils";
import module namespace dfstcnst="http://dita-for-small-teams.org/xquery/modules/dfst-constants";
import module namespace relpath="http://dita-for-small-teams.org/xquery/modules/relpath-utils";

declare variable $lmutil:useMatchParamsLocalMaps :=
                               map{'linktype' :'topicref',
                                   'format' : 'ditamap',
                                   'scope' : 'local'
                                  };
declare variable $lmutil:useMatchParamsPeerMaps :=
                               map{'linktype' :'topicref',
                                   'format' : 'ditamap',
                                   'scope' : 'peer'
                                  };

(:~
 : Find all links that use direct URI references to their target resources.
 : Returns list of link item maps
 :
 : Each link item map has the following items:
 :
 : 'link' : The element that is the link
 : 'rootMap' : The root map document that defines the keyspace the link
 :             is resolved in.
 : 'resolvedMap' : The fully-resolved map that defines key space the key
 :                 reference is resolved in.
 : 'keySpace' : The constructed key space for the root map.
 : 'processingRole' : For topicrefs, the @processing-role value. For
 :                    other links, 'normal'.
 : 'linkContext': The relevant container type for the link, one of:
 :                - 'title' : Link occurs within a title element
 :                - 'prolog' : Link occurs with topicmeta (maps) or prolog (topics)
 :                - 'shortdesc' : Link occurs within a short description
 :                - 'abstract' : Link occurs within an abstract (but not within a
 :                               short description within an abstract)
 :                - 'body' : Link occurs within a topic body
 :                - 'reltable' : Link is a topicref within a relationship table.
 :                - 'navtree' : Link is a topicref with @processing-role of 'normal' and
 :                              is not within a relationship table
 :                - 'resources' : Link is a topicref with a processing role of "resource-only".
 :  'isDirect' : - 'true' if link is a direct URI reference
 :               - 'false' if link uses a resolvable key reference
 :
 : For direct links the link element is sufficient
 : to allow resolution as use context has no affect on how the link is resolved.
 : For "." ("this topic") fragment IDs, the context is always the containing topic.
 :
 : @param dbName Name of the database that contains the documents to be processed.
 : @param logID ID of the log to put log messages in.
 :)
declare function lmutil:findAllDirectLinks($dbName) as map(*)* {
  let $db := db:open($dbName)
  (: First do direct URI references, which don't require any
     context knowledge:
   :)
  let $links := collection($dbName)//*[df:isTopicRef(.) and not(@keyref)] |
                collection($dbName)//*[contains(@class, ' topic/xref ') and (@href and not(@keyref))] |
                collection($dbName)//*[contains(@class, ' topic/data-about ') and (@href and not(@keyref))] |
                collection($dbName)//*[contains(@class, ' topic/longdescref ') and (@href and not(@keyref))] |
                collection($dbName)//*[@conref]
   (: Construct a link item map for each link element :)
   for $link in $links
       return lmutil:constructLinkItemMap($link)
};

(:~
 : Constructs a link item map for a direct link.
 :
 : @param link The link element
 : @return Link item map
 :)
declare function lmutil:constructLinkItemMap(
                        $link as element()) as map(*) {
  let $contentDocURI := document-uri(root($link))
  let $result := lmutil:constructLinkItemMap($link, $contentDocURI, ())
  return $result
};

(:~
 : Constructs a link item map
 :
 : @param link The link element
 : @param topicref For indirect links, the topicref that establishes
 :                 the key resolution context for the link. Not set for direct URI links.
 :                 Note that this topicref element must be from the resolved map, not the
 :                 original source map.
 :)
declare function lmutil:constructLinkItemMap(
                        $link as element(),
                        $contentDocURI as xs:string,
                        $topicref as element()?) as map(*) {
   let $linkItem :=
      map{'link': $link,
          'resolvedMap': if ($topicref) then root($topicref)/* else (),
          'topicref' : $topicref,
          'contentDocURI' : $contentDocURI,
          'processingRole':
             if (df:isTopicRef($link))
                then if ($link/@processing-role)
                        then string($link/@processing-role)
                        else 'normal' (: Default for topicref :)
                else 'normal', (: non-topicref links :)
          'linkContext': lmutil:getLinkContext($link)
         }
    return $linkItem
};

(: Give a document, finds all references to that document that match the
   type of uses as configured in the $useParams.

   @param doc Document to find uses of.
   @param useParams Use filter parameters. Only uses that match the parameters
                    will be reported.
   @return Zero or more use record elements.

   The use parameters are:

   linktype: List of base type names (e.g., 'topicref, xref) or qualified
             class names (e.g., 'map-d/navref') of the types of links
             to report usage for. If unspecifed, all link types are reported.
             The keyword "#conref" indicates uses via @conref or @conkeyref

   format:   List of @format values by which the document is used, e.g. "dita", "ditamap",
             etc. If unspecified, uses are not filtered by @format value.

   scope:    List of @scope values to filter the uses by. If unspecified, uses are not
             filtered by @scope value.

   TBD: Need for direct/indirect filter, other filters.

   FIXME: Need to rationalize the coding pattern for use parameters and
          their use by the useRecordMatcher() function. The code is a little
          confused right now as a result of some quick refactoring.

 :)
declare function lmutil:getUses($elem as element(), $useParams) as element()* {

   let $linktypes := if ($useParams('linktype'))
                        then $useParams('linktype')
                        else ('#any')
   let $formats   := if ($useParams('format'))
                        then $useParams('format')
                        else ('#any')
   let $scopes   := if ($useParams('scope'))
                        then $useParams('scope')
                        else ('#any')

   (: Note that all references are ultimately to elements,
      so resource keys are always for elements, not
      documents. Given an element we can always get its
      containing document.

      In DITA, except for <dita> documents, references to
      documents with no fragment identifier are implicitly
      to the root elements of those documents (i.e., a map
      or topic element).

      The resource key here is used as the name of the
      directory that contains the where-used records for
      this element.
    :)
   let $resKey := lmutil:constructResourceKeyForElement($elem)

   (: Now find all use records for the resource key that match the filter
      specification.

      :)
    let $dbName := bxutil:getMetadataDbNameForDoc(root($elem))
    let $collection := $dbName || $dfstcnst:where-used-dir || '/' || $resKey
    let $records := collection($collection)
                       /dfst:useRecord[lmutil:useRecordMatcher(., $linktypes, $formats, $scopes)]
    return $records

};

(: Given an element, construct the unique resource key for it.
   For a given element the resource ID is guaranteed to be unique within
   a snapshot (git commit).

   @param elem Element to get resource ID for.
   @returns Resource key string.

   The resource key is a combination of the absolute URI of the containing
   document and the element's tree position within the document, producing
   a unique key for any element in any document on a given snapshot (version
   in time). Resource keys are not reliably unique across snapshots as the position of
   the element within its containing document could change from version to
   version.

   The resource key is used to look up the element in where-used records, keyspaces,
   and other link management metadata indexes, either
   as the element used or the element doing the use (links).

 :)
declare function lmutil:constructResourceKeyForElement($elem as element()) as xs:string {
   lmutil:constructResourceKeyForElement(document-uri(root($elem)), $elem)
};

(: Given an element, construct the unique resource key for it.
   For a given element the resource ID is guaranteed to be unique within
   a snapshot (git commit).

   @param docURI URI of the document that contains the element.
   @param elem Element to get resource ID for.
   @returns Resource key string.

   The resource key is a combination of the absolute URI of the containing
   document and the element's tree position within the document, producing
   a unique key for any element in any document on a given snapshot (version
   in time). Resource keys are not reliably unique across snapshots as the position of
   the element within its containing document could change from version to
   version.

   The resource key is used to look up the element in where-used records, either
   as the element used or the element doing the use (links).

 :)
declare function lmutil:constructResourceKeyForElement(
                                        $docURI as xs:string,
                                        $elem as element()) as xs:string {
  let $targetDocHash := replace(string(hash:md5($docURI)), '/', '~')
  let $treepos := for $anc in ($elem/ancestor-or-self::*)
                      return string(count($anc | $anc/preceding-sibling::*))
  let $key := string-join($treepos, '.')
  return $targetDocHash || '^' || $key

};

(:~
 : Determines if a given where-used record matches the filter
 : specified parameters.
 :
 : FIXME: Use a map to pass the filter parameters rather than
 : individual arguments.
 :)
declare function lmutil:useRecordMatcher($record as element(),
                                         $linktypes as xs:string*,
                                         $formats as xs:string*,
                                         $scopes as xs:string*) as xs:boolean {
   let $result := ((if ($linktypes = '#any')
                       then true()
                       else string($record/@linkType) = $linktypes) and
                   (if ($formats = '#any')
                       then true()
                       else string($record/@format) = $formats) and
                   (if ($scopes = '#any')
                       then true()
                       else string($record/@scope) = $scopes))
  return $result
};

(:~
 : Given a link element, determines the linkContext value for it.
 :)
declare function lmutil:getLinkContext($link as element()) as xs:string {
  if (df:class($link, 'map/topicref'))
     then if ($link/ancestor::*[df:class(., 'map/reltable')])
             then 'reltable'
          else if ($link/@processing-role = ('resource-only'))
               then 'resources'
          else 'navtree'
     else if ($link/ancestor::*[df:class(., 'topic/title')])
          then 'title'
     else if ($link/ancestor::*[df:class(., 'topic/shortdesc')])
          then 'shortdesc'
     else if ($link/ancestor::*[df:class(., 'topic/abstract')])
          then 'abstract'
     else if ($link/ancestor::*[df:class(., 'topic/prolog') or df:class(., 'map/topicmeta')])
          then 'prolog'
     else if ($link/ancestor::*[df:class(., 'topic/body')])
          then 'body'
     else 'unknown-context'

};

(:~
 : Given a link item map, attempts to resolve the link to an element (in DITA a given link
 : can address at most one element).
 :
 : Returns a map with the following members:
 : 'target': A sequence of zero or more elements addressed
 :           by the link.
 : 'log':    A sequence of zero or more log entry elements
 :           generated by the resolution attempt.
 :)
declare function lmutil:resolveDirectLink($linkItem as map(*)) as map(*) {

   let $link as element()? := $linkItem('link')
   return if ($link)
      then
       let $resultMap := if (df:class($link, 'map/topicref') and ($link/@href))
                            then df:resolveTopicRef($link)
                            else df:resolveNonTopicRefDirectLink($link)

       let $targets := $resultMap('target')
       let $log := (<info>Link: {
                   concat('<',
                          name($link), ' ',
                          lmutil:reportAtts($link, ('href', 'keyref', 'keys', 'conref', 'scope')),
                          '>')} [class: "{string($link/@class)}"], doc: "{document-uri(root($link))}"</info>,
                  if ($targets)
                     then <info>  Link resolved</info>
                     else $resultMap('log')
                  )
       return map{'target' : $targets, 'log' : $log, 'link' : $linkItem}
     else
       map{'target' : (),
                 'log' : (),
                 'link' : $linkItem
                }
};

(: Given a link descriptor map and the database that contains it, attempts
   to resolve the link to an element (in DITA a given link
   can address at most one element).

   Returns a map with the following members:
   'target': A sequence of zero or more elements addressed
             by the link.
   'log':    A sequence of zero or more log entry elements
             generated by the resolution attempt.
   'link':   The input link item.
 :)
declare function lmutil:resolveIndirectLink(
                        $metadataDbName as xs:string,
                        $linkItem as map(*)) as map(*) {

   let $topicref := $linkItem?topicref (: Topicref in resolved map :)
   let $link := $linkItem?link
   let $keyName := df:getKeyNameForKeyref($link)
   let $rootMap := $linkItem?rootMap
   let $contentMapURI := string(root($topicref)/*/@origMapURI)

   (: Given the key name, look it up in the topicref's map (which must be a
      resolved map to find the key definition. The resolved map has had
      all key names expanded with their scope qualifications, so we can
      look up scope-qualified keys directly. :)
   let $keydef := lmutil:findKeyDefinition($metadataDbName, $keyName, $contentMapURI, $topicref)
   let $targets :=
       if ($keydef)
          then lmutil:resolveTopicRefFromResolvedMap($keydef)
          else ()

   let $log := (if (not($keydef))
                   then <warn>No key definition found for key "{$keyName}"</warn>
                   else ()
               )
   return map{'target' : $targets,
              'log' : $log ,
              'link' : $linkItem,
              'keydef' : $keydef
             }
};

(:~
 : Given a key name and a topicref in a resolved map, find the applicable
 : key-defining topicref, if any.
 :
 : @param keyName The key name to look up.
 : @param topicref The topicref that establishes the map context (and thus
 :                 the starting key scope for the lookup)
 : @return The key-defining topicref or empty sequence if no key definition
 :             is found.
 :)
declare function lmutil:findKeyDefinition(
                            $metadataDbName as xs:string,
                            $keyName as xs:string,
                            $contentMapURI as xs:string,
                            $topicref as element()) as element()? {
  (: Given a topicref, find the key space that it contributes to,
     then find the effective key binding for the key name within
     the key space hierarchy.
   :)
  let $topicrefID := lmutil:constructResourceKeyForElement($contentMapURI, $topicref)
  let $keyDefInKeySpace := collection($metadataDbName ||
    $dfstcnst:keyspaces-dir)//keyspace/keys/key[@name = $keyName]/*[@resID = $topicrefID]

  let $keys := for $keyspace in $keyDefInKeySpace/ancestor::keyspace
                   return $keyspace/keys/key[@name = $keyName]
  let $effectiveKeyDef := $keys[1]/*[1]

  let $result := $effectiveKeyDef
  return $result
};

(:~
 : Given a database, finds all the indirect links. Returns a map
 : the the members.
 : @param dbName Name of the content database to find the links for
 : @return Map with two members:
 :
 : 'links' : A sequence of linkItem maps, where each map represents one link.
 : 'log' : A sequence of log entry elements.
 :
 :
 :)
declare function lmutil:findAllIndirectLinks($dbName) as map(*)* {
   (:

1. Find all root maps (maps with no local-scope topicref references or
   maps with peer topicref references). This requires that the direct-reference
   use records are up to date. It also requires that the resolved map and key space
   documents have already been created.

2. For each root map, Walk the resolved map, creating link items for each key-based topicref.
   Resolve topicrefs to topics and process each topic to create link items for each key-based
   link in each topic.

   :)

   let $rootMaps as element()* := lmutil:getRootMaps($dbName)

   let $linksFromTopics := lmutil:getIndirectLinksFromTopics($rootMaps)

    let $linksFromMaps := () (: FIXME: Build this list as well :)
    let $links := ($linksFromTopics, $linksFromMaps)

   return $links
};

(:~
 : Get all the indirect links in topics referenced from the root maps
 :
 : @para rootMaps list of root content maps.
 :
 : @return Sequence, possibly empty, of link item maps.
 :)
declare function lmutil:getIndirectLinksFromTopics($rootMaps as element()*) as map(*)* {
  let $result :=
     for $map in $rootMaps
         let $resolvedMap as element()? := lmutil:getResolvedMapForMap($map)
         return if ($resolvedMap)
            then
               let $bos as map(*)* := lmutil:getMapBoundedObjectSet($resolvedMap, ('dita'))
               return for $member in $bos
                  return lmutil:getIndirectLinks($bos)
            else ()
  return $result
};

(:~
 : Takes a sequence of map BOS members and, for each member,
 : finds the key-based links in the member and returns a of link
 : structures reflecting those links.
 :
 : @param bos Sequence of maps, one for each use of a topic from a
 : DITA map. Each map has two members, "topicref", and "resource"
 : @return A sequence, possibly empty, of link maps.
 :)
declare function lmutil:getIndirectLinks($bos as map(*)*) as map(*)* {
   let $result as map(*)* :=
       for $member in $bos
           return lmutil:getIndirectLinksForBOSMember($member)
   return $result
};

(:~
 : Given a BOS member map for a topic, finds all key-based links in the
 : topic and returns a sequence of link items for the links.
 :
 : @param member BOS member map containing a topic BOS member.
 : @return A sequence, possibly empty, of link items.
 :)
declare function lmutil:getIndirectLinksForBOSMember($member as map(*)) as map(*)* {
   let $topicref := $member('topicref')
   let $topic := $member('resource')
   let $contentDocURI := "contentdocURI value"
   let $linkElems := $topic//*[@keyref]
   let $linkItems :=
       for $elem in $linkElems
           return lmutil:constructLinkItemMap($elem, $contentDocURI, $topicref)
   return $linkItems
};

(:~
 : Given a map resolves all the topicrefs to their local-scope resources
 : and returns the set of "BOS members", where each member is a map
 : with the topicref that pointed to the topic and the resolved topic.
 : The topicref is required in order to have the map context, which is required
 : in order to determine the key space within which any key references within the
 : topic are resolved. Note that a BOS member is a unique topicref/resource pair.
 : If the same resource is referenced multiple times in the map there will be one BOS member
 : for each reference.
 :
 : @param map Map to process. Does not resolve submap references, so if
 : you want the resources for a map tree supply the resolved map.
 :
 : @param formats A list of one or more @format values to return in the set.
 : The special value '#non-dita' matches any @format value that is not 'dita'
 : or 'dita-map'. Specify ('dita') to return only topic members of
 : the BOS. Non-DITA resources are represented by the topicrefs that point to
 : them. If not specified, all local-scope resources are returned.
 : @return Sequence of maps, one for each topicref/resource pair
 :)
declare function lmutil:getMapBoundedObjectSet(
                            $map as element(),
                            $formats as xs:string*) as map(*)* {
  let $topicrefs := $map//*[df:isTopicRef(.)]
                           [df:isLocalScope(.)]
                           [not($formats) or
                            df:getEffectiveFormat(.) = $formats]
  let $members :=
      for $topicref in $topicrefs
          let $resource := if (df:getEffectiveFormat($topicref) = ('dita'))
             then lmutil:resolveTopicRefFromResolvedMap($topicref)
             else $topicref
          return map { 'topicref' : $topicref,
                       'resource' : $resource }
  return $members

};

(:~
 : Resolves a topicref from a resolved map. Uses the additional metadata
 : in the resolved map to determine the target document that contains
 : the element.
 :)
declare function lmutil:resolveTopicRefFromResolvedMap($topicref as element())
                                                                    as element()? {
  let $baseURI := string(($topicref/ancestor::*[@origMapURI]/@origMapURI)[1])
  let $targetURI := string($topicref/@href)
  let $resolvedURI := relpath:resolveURI($targetURI, $baseURI)
  let $result := try {
    let $doc as document-node()? := doc($resolvedURI)
    let $elem := if ($doc) then $doc/* else()
    return $elem
  } catch * {
    (: Document not found :)
    ()
  }
  return $result
};

(:~
 : Find maps that either have no local-scope topicrefs to them or that have
 : any peer-scope topicrefs to them.
 :
 : @param dbName Name of the content database that holds the map to examine.
 : @return List, possibly empty, of root maps.
 :)
declare function lmutil:getRootMaps($dbName as xs:string) as element()* {
  let $candMaps := collection($dbName)/*[df:class(., 'map/map')]
  let $result :=
      for $candMap in $candMaps
          return if (lmutil:isRootMap($candMap))
                   then $candMap
                   else ()
  return $result
};

(:~
 : Returns true if the specified map element is a root map.
 :
 : Note that there are many ways that root mapness could be determined.
 : The logic used here is any map that has no direct map local-scope references
 : or any peer-scope references. This is based on the presumption that a root
 : map would not normally be useable as local-scope submap. DITA 1.3's new
 : meaning for scope="peer" on topicrefs to maps explicitly means "the referenced
 : map is a root map".
 :
 : Other options would be to have a master map that uses peer map references to
 : explicitly identify root maps and use only that to determine rootness, to
 : maintain some sort of metadata that indicates rootness, use a specific map
 : specialization or map specialization plus descendant element configuration
 : (e.g., bookmaps that have booktitle elements are root maps, all others are
 : not).
 :
 : @param map Map to check for rootness
 : @return true() if the map is a root map.
 :)
declare function lmutil:isRootMap($map as element()) as xs:boolean {
  let $resolvedMap as element()? := lmutil:getResolvedMapForMap($map)
  let $result :=
      if (not($resolvedMap)) (: If no resolved map, link management database hasn't
                                been initialized and have to assume all maps are root:)
         then true()
         else ((lmutil:getRefCount($map, $lmutil:useMatchParamsLocalMaps) = 0) or
               (lmutil:getRefCount($map, $lmutil:useMatchParamsPeerMaps) > 0))
  return $result
};

(:~
 : Gets the number of references to the specified element that match the specified
 : match criteria.
 :
 : @param elem Element to get reference count for.
 : @param matcher Use record matcher to use for selecting references
 : @return Number of matches
 :)
declare function lmutil:getRefCount($elem as element(),
                                    $matchParams as map(*)) as xs:integer {
  let $uses := lmutil:getUses($elem, $matchParams)
  let $result := count($uses)
  return $result
};

(: Construct a string report of the listed attributes :)
declare function lmutil:reportAtts($elem as element(), $attNames) as xs:string {
   let $result := for $att in $elem/@*
                      return if (name($att) = $attNames)
                                then concat(name($att), '="', string($att), '"')
                                else ()
   return string-join($result, ' ')
};

(:~
 : Report a link item map as XML
 :
 : @param linkItem
 : @return XML representation of the link
 :)
declare function lmutil:reportLinkItemMap($linkItem as map(*)) as element() {
(:
 : 'link' : The element that is the link
 : 'rootMap' : The root map document that defines the namespace the link
 :             is resolved in.
 : 'resolvedMap' : The fully-resolved map that defines key space the key
 :                 reference is resolved in.
 : 'keySpace' : The constructed key space for the root map.
 :)
  <linkItem>{
     bxutil:reportMapAsXML($linkItem)
}</linkItem>
};

(:~
 : Get the resolved map document for a map. The resolved map must have already
 : been constructed (XQuery doesn't let use do updating actions wherever we
 : want).
 :
 : @param map Content map to get the resolved map for.
 : @return the resolved map or an empty sequence if there is no resolved map
 : (for example, because the map is not a root map).
 :)
declare function lmutil:getResolvedMapForMap($map as element()) as element()? {
  let $resolvedMapURI as xs:string := lmutil:getResolvedMapURIForMap($map)
  let $metadataDbName as xs:string := bxutil:getMetadataDbNameForDoc(root($map))

  let $resolvedMap := collection($metadataDbName || $resolvedMapURI)
  return $resolvedMap/*
};

(:~
 : Construct the database URI to use for a resolved map.
 :
 : @param map DITA map element to get the URI for
 : @returns The URI of the resolved map as a string
 :)
declare function lmutil:getResolvedMapURIForMap(
                        $map as element()) as xs:string {
  let $mapDocHash := replace(string(hash:md5(document-uri(root($map)))), '/', '~')
  return $dfstcnst:resolved-map-dir ||
         "/" || $mapDocHash || ".ditamap"
};

(:~
 : Given a keyspace element return the document URI to use when
 : storing or finding the key space.
 :
 : @param keyspace Element that is the key space.
 : @return The URI for the key space document.
 :)
declare function lmutil:getKeySpaceURIForKeySpace(
                            $keySpace as element()) as xs:string {
  let $resolvedMapURI as xs:string? := string($keySpace/@resolvedMap)
  let $result := lmutil:getKeySpaceURIForResolvedMapURI($resolvedMapURI)
  return $result
};

(:~
 : Given the URI of a resolved map, construct the URI for the corresponding key space document
 :
 :)
declare function lmutil:getKeySpaceURIForResolvedMapURI($resolvedMapURI as xs:string) as xs:string {
  let $filenameBase := relpath:getNamePart($resolvedMapURI)
  let $uri :=
            let $parentDir := relpath:getParent($resolvedMapURI)
            return relpath:newFile($parentDir,
               concat($filenameBase, '.keyspace'))
  return $uri
};

(: End of Module :)


removing hello world file

removing hello world file

module namespace page = 'http://basex.org/modules/web-page';

import module namespace csd_dm = "https://github.com/openhie/openinfoman/csd_dm";
import module namespace csd_webui =  "https://github.com/openhie/openinfoman/csd_webui";

declare updating
  %rest:path("/CSD/createDirectory")
  %rest:POST
  %rest:form-param("directory","{$directory}")
  %output:method("xhtml")
  function page:create_directory($directory)
{
   (
     if (not (csd_dm:document_source_exists($directory))) then
       csd_dm:empty($directory)
     else ()
     ,
   csd_webui:redirect_out("CSD")
  )
};

declare
  %rest:path("/CSD")
  %rest:GET
  %output:method("xhtml")
  function page:list_functionality()
{
  let $what_is_dm :=

    The document manger registers documents to perform  careServicesRequests and getUpdatedServices requests  against.  Registered documents can be any of:

Local cache of a remote service directory -- this is useful for turning a Care Services Directory into an Open Info Manager

Sample Directories -- these loaded from an XML source file on the system and are useful for testing

The merged result of remote service directories

  let $csd :=

    {$what_is_dm}

      {let $docs := csd_dm:registered_documents()
      return (
	  "You have ", count($docs) , " registered document(s) available: ",

	    {
	      for $doc in $docs
	      return

		 {$doc}
		 [
		   Empty
		  /  Delete
		  ]

	    }

	  )
      }

Create Blank Directory

      <form method='post' action="{csd_webui:generateURL(('CSD/createDirectory/'))}">
	<input name='directory' type='text'/>
	<input type='submit' value='Create'/>
      </form>

  let $svs :=

    In addition, there is some initial support for use of terminologies using the Sharing Value Sets(SVS) profile from IHE:

load Shared Value Sets

available Shared Value Sets

mirror remote Shared Value Set

  let $adapters :=

        {
	  let $docs := csd_dm:registered_documents()
	  return (
	    "You have ", count($docs) , " registered document(s) available: ",

	      {
		for $doc in $docs
		return

		   {$doc}
		  [
		   View Stored Functions
		  ]

	      }

	  )
	}

List of all API  adapters for stored functions: CSD Adapters

Facility Merging

  let $generic :=

These the top-level endpoints are exposed

Manage Stored Functions

Load and Register Sample Service Directories

Register and Poll Remote Service Service directories

Execute Ad-Hoc Care Services Requests and View Care Service Request Endpoints for Registered Documents

Endpoints for submitting getUpdatedServices Soap Request for Registered Documents

Manage duplicate record identifiers

  return csd_webui:nocache(page:wrapper($csd,$svs,$adapters,$generic))
};

declare
  %rest:path("/CSD/getDirectory/{$name}")
  %rest:GET
  function page:get_directory($name)
{
  csd_dm:open_document($name)
};

declare updating
  %rest:path("/CSD/emptyDirectory/{$name}")
  %rest:GET
  function page:empty_directory($name)
{
  (
    csd_dm:empty($name)
    ,csd_webui:redirect_out("CSD")
  )
};

declare updating
  %rest:path("/CSD/deleteDirectory/{$name}")
  %rest:GET
  function page:delete_directory($name)
{
  (
    csd_dm:delete($name)
    ,csd_webui:redirect_out("CSD")
  )
};

declare function page:wrapper($csd,$svs,$adapters) {
  page:wrapper($csd,$svs,$adapters,())
};

declare function page:wrapper($csd,$svs,$adapters,$generic_csd) {
  let $generic :=

OpenInfoMan has been developed as part of OpenHIE and is intended to be the engine behind the CSD compliant Health Worker Registry and to be incorporated in OpenHIM.

    Source code is on github

  let $headers :=  (
    <link rel="stylesheet" type="text/css" media="screen"   href="{csd_webui:generateURL('static/bootstrap/js/tab.js')}"/>
    ,

    )
    let $content := (

Introduction

Server Managment

Terminolgies

Documents

      ,

Welcome to OpenInfoMan

		  OpenInfoMan is an XQuery and RESTXQ based implementation of the Care Services Directory (CSD) profile from IHE which implements the following actors and transactions:

		    Info Manager :

		    Find Matching Services (Ad-Hoc and Stored) [ITI-73]

		    Query for  Updated Services Transaction [ITI-74]

		    Services Directory :

Query for  Updated Services Transaction [ITI-74]

		{$generic}

	      {$generic}
	      {$generic_csd}

	      {$csd}

	      {$generic}

	      {$svs}

	      {$generic}

	      {$adapters}

      )

  return csd_webui:wrapper($content,$headers)
};


[fix] add license header to XQSuite

[fix] add license header to XQSuite

(:
 : eXist-db Open Source Native XML Database
 : Copyright (C) 2001 The eXist-db Authors
 :
 : info@exist-db.org
 : http://www.exist-db.org
 :
 : This library is free software; you can redistribute it and/or
 : modify it under the terms of the GNU Lesser General Public
 : License as published by the Free Software Foundation; either
 : version 2.1 of the License, or (at your option) any later version.
 :
 : This library is distributed in the hope that it will be useful,
 : but WITHOUT ANY WARRANTY; without even the implied warranty of
 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 : Lesser General Public License for more details.
 :
 : You should have received a copy of the GNU Lesser General Public
 : License along with this library; if not, write to the Free Software
 : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 :)
xquery version "3.1";

(: https://github.com/eXist-db/exist/issues/3737 :)
module namespace fn-pos="http://exist-db.org/xquery/test/fn-position";

declare namespace test="http://exist-db.org/xquery/xqsuite";

declare variable $fn-pos:collection := 'test-fn-pos';
declare variable $fn-pos:doc := 'stored-nodes.xml';
declare variable $fn-pos:nodes := document {
    <a>
      <b n="a"/>
      <b n="b"/>
      <b n="c"/>
    </a>
};

declare
    %test:setUp
function fn-pos:setup () {
    xmldb:create-collection('/db', $fn-pos:collection),
    xmldb:store('/db/' || $fn-pos:collection, $fn-pos:doc, $fn-pos:nodes)
};

declare
    %test:tearDown
function fn-pos:teardown () {
    xmldb:remove('/db/' || $fn-pos:collection, $fn-pos:doc)
};

declare
    %test:assertEquals(1,1,2,3)
function fn-pos:test-in-memory-dom() {
    $fn-pos:nodes/a/position(),
    $fn-pos:nodes//b/position()
};

declare
    %test:assertEquals(1,1,2,3)
function fn-pos:test-in-memory-dom-with-predicates() {
    $fn-pos:nodes/a[true()]/position(),
    $fn-pos:nodes//b[true()]/position()
};

declare
    %test:assertEquals(1,1)
function fn-pos:test-in-memory-dom-with-positional-predicates() {
    $fn-pos:nodes/a[1]/position(),
    $fn-pos:nodes//b[3]/position()
};

declare
    %test:assertEquals(1,1,2,3)
function fn-pos:test-persistent-dom() {
    let $dom := doc('/db/' || $fn-pos:collection || '/' || $fn-pos:doc)

    return (
        $dom/a/position(),
        $dom//b/position()
    )
};

declare
    %test:assertEquals(1,1,2,3)
function fn-pos:test-persistent-dom-with-predicates() {
    let $dom := doc('/db/' || $fn-pos:collection || '/' || $fn-pos:doc)

    return (
        $dom/a[true()]/position(),
        $dom//b[true()]/position()
    )
};

declare
    %test:assertEquals(1,1)
function fn-pos:test-persistent-dom-with-positional-predicates() {
    let $dom := doc('/db/' || $fn-pos:collection || '/' || $fn-pos:doc)

    return (
        $dom/a[1]/position(),
        $dom//b[3]/position()
    )
};


Check whether the group exists before creating it

svn path=/trunk/eXist/; revision=13005

xquery version "1.0";
(: $Id$ :)
(:
    Module: examples setup
:)

module namespace setup="http://exist-db.org/xquery/admin-interface/setup";

declare namespace xdb="http://exist-db.org/xquery/xmldb";
declare namespace request="http://exist-db.org/xquery/request";
declare namespace session="http://exist-db.org/xquery/session";
declare namespace util="http://exist-db.org/xquery/util";
declare namespace system="http://exist-db.org/xquery/system";

declare namespace xproc="http://xproc.net/xproc";

declare function setup:main() as element()
{

Examples Setup

        {
let $action := request:get-parameter("action", ()) return
		if($action) then
		                (
		                    if($action eq "Import Example Data") then
		                    (
		                        setup:importLocal()
		                    )
		                    else if($action eq "Import Files") then
		                    (
		                        setup:importFromURLs()
		                    )
		                    else
		                    (
		                        setup:report()
		                    )
		                )
		                else
		                (
		                    setup:select()
		                )

        }

};

declare function setup:importLocal() as element()+
{
    let $home := system:get-exist-home(),
    $pathSep := util:system-property("file.separator"),
    $dir := if (doc-available(concat("file:///", $home, "/exist-samples/examples.xml"))) then
        (
            concat($home, $pathSep, "exist-samples")
        )
        else if(ends-with($home, "WEB-INF")) then
        (
            concat(substring-before($home, "WEB-INF"), "samples")
        )
        else
        (
            concat($home, $pathSep, "samples")
        ),
    $webapp := if (doc-available(concat("file:///", $home, "/webapp/index.xml"))) then
        (
            concat($home, $pathSep, "webapp")
        )
        else if(ends-with($home, "WEB-INF")) then
        (
            substring-before($home, "WEB-INF")
        )
        else
        (
            concat($home, $pathSep, "webapp")
        )
        (: ,
        $betterform := concat($home, "/extensions/betterform/src/main/xrx/timetracker/")
        :)
    return (
                setup:create-collection("/db/system/config", "db"),
                setup:create-collection("/db/system/config/db", "shakespeare"),
                setup:store-files("/db/system/config/db/shakespeare", $dir,
                    "shakespeare/collection.xconf", "text/xml"),
                setup:create-collection("/db", "shakespeare"),
                setup:create-collection("/db/shakespeare", "plays"),
                setup:store-files("/db/shakespeare/plays", $dir,  "shakespeare/*.xml", "text/xml"),
                setup:store-files("/db/shakespeare/plays", $dir, "shakespeare/*.xsl", "text/xml"),
                setup:store-files("/db/shakespeare/plays", $dir, "shakespeare/*.css", "text/css"),
                setup:create-collection("/db", "xinclude"),
                setup:create-collection("/db/xinclude", "styles"),
                setup:store-files("/db/xinclude", $dir, "xinclude/*.xsl", "text/xml"),
                setup:store-files("/db/xinclude", $dir, "xinclude/*.xml", "text/xml"),
                setup:store-files("/db/xinclude", $dir, "xinclude/*.xq", "application/xquery"),
                setup:store-files("/db/xinclude", $dir, "xinclude/*.jpg", "image/jpeg"),
                setup:store-files("/db/xinclude/styles", $dir, "xinclude/styles/*", "text/css"),
                setup:create-collection("/db/xinclude", "scripts"),
                setup:create-collection("/db/xinclude/scripts", "syntax"),
                setup:store-files("/db/xinclude/scripts/syntax", $dir, "xinclude/scripts/syntax/*.js", "application/x-javascript"),
                setup:create-collection("/db", "library"),
                setup:store-files("/db/library", $dir, "*.rdf", "text/xml"),
                setup:create-collection("/db/system/config/db", "mods"),
                setup:store-files("/db/system/config/db/mods", $dir, "mods/collection.xconf", "text/xml"),
                setup:create-collection("/db", "mods"),
                setup:create-collection("/db/mods", "eXist"),
                setup:create-collection("/db/mods", "samples"),
                setup:create-collection("/db/mods", "users"),
                setup:create-group("biblio.users"),
                setup:set-collection-group("/db/mods/users", "biblio.users"),
                setup:set-collection-permissions("/db/mods/users", "rwurwur--"),
                setup:create-collection("/db/mods", "groups"),
                setup:set-collection-group("/db/mods/groups", "biblio.users"),
                setup:set-collection-permissions("/db/mods/groups", "rwurwu---"),
                setup:store-files("/db/mods/samples", $dir, "mods/*.xml", "text/xml"),
                setup:store-files("/db/mods/eXist", $dir, "mods/eXist/*.xml", "text/xml"),
                setup:store-files("/db", $dir, "*.xml", "text/xml"),
                setup:create-collection("/db/system/config/db", "mondial"),
                setup:store-files("/db/system/config/db/mondial", $dir, "mondial.xconf", "text/xml"),
                setup:create-collection("/db/system/config/db", "xmlad"),
                setup:store-files("/db/system/config/db/xmlad", $dir, "acronyms.xconf", "text/xml"),

                (: Storing Testing :)
                setup:create-collection("/db", "testing"),
                setup:store-files("/db/testing", $webapp, "testing/*.xml", "text/html"),
                setup:store-files("/db/testing", $webapp, "testing/*.xq*", "application/xquery"),
                setup:create-collection("/db/testing", "examples"),
                setup:store-files("/db/testing/examples", $webapp, "testing/examples/*.xml", "text/html"),
                setup:store-files("/db/testing/examples", $webapp, "testing/examples/*.xq*", "application/xquery"),

                (: Storing XProc :)
                setup:create-collection("/db", "xproc"),
                setup:create-collection("/db/xproc", "unit-test"),
                setup:create-collection("/db/xproc", "xml"),
                setup:create-collection("/db/xproc", "xslt"),
                setup:create-collection("/db/xproc", "result"),
                setup:create-collection("/db/xproc", "examples"),
                setup:create-collection("/db/xproc", "pipelines"),
                setup:store-files("/db/xproc/unit-test", $webapp, "xproc/unit-test/*.xml", "text/xml"),
                setup:store-files("/db/xproc/unit-test", $webapp, "xproc/unit-test/*.xql", "application/xquery"),
                setup:store-files("/db/xproc/unit-test", $webapp, "xproc/unit-test/*.xsl", "text/xml"),

                setup:store-files("/db/xproc/pipelines", $webapp, "xproc/pipelines/*.xml", "text/xml"),
                setup:store-files("/db/xproc/xml", $webapp, "xproc/xml/*.xml", "text/xml"),
                setup:store-files("/db/xproc/xslt", $webapp, "xproc/xml/*.xsl", "text/xml"),

                setup:store-files("/db/xproc/examples", $webapp, "xproc/examples/*.xproc", "application/xml+xproc"),
                setup:store-files("/db/xproc/examples", $webapp, "xproc/examples/*.xml", "text/xml"),

                setup:store-files("/db/xproc/examples", $webapp, "xproc/examples/*.xql", "application/xquery"),
                setup:store-files("/db/xproc", $webapp, "xproc/*.xml", "text/xml"),
                setup:store-files("/db/xproc", $webapp, "xproc/*.xql", "application/xquery") (:,

                (: Storing XForms in the db won't work right now :)

                setup:create-collection("/db", "xforms"),
                setup:store-files("/db/xforms", $webapp, "xforms/*.xml", "text/html"),
                setup:store-files("/db/xforms", $webapp, "xforms/*.xq*", "application/xquery"),
                setup:create-collection("/db/xforms", "xsltforms"),
                setup:create-collection("/db/xforms", "data"),
                setup:store-files("/db/xforms/xsltforms", $webapp, "xforms/xsltforms/*.properties", "text/plain"),
                setup:store-files("/db/xforms/xsltforms", $webapp, "xforms/xsltforms/*.png", "image/png"),
                setup:store-files("/db/xforms/xsltforms", $webapp, "xforms/xsltforms/*.gif", "image/gif"),
                setup:store-files("/db/xforms/xsltforms", $webapp, "xforms/xsltforms/*.xsl","text/xml"),
                setup:store-files("/db/xforms/xsltforms", $webapp, "xforms/xsltforms/*.js","application/x-javascript"),
                setup:store-files("/db/xforms/xsltforms", $webapp, "xforms/xsltforms/*.css","text/css")
                :)

				(: Storing betterFORM XRX sample application :)
				(:
				setup:create-collection("/db", "betterform"),
				setup:create-collection("/db/betterform", "apps"),
				setup:create-collection("/db/betterform/apps", "timetracker"),
				setup:store-files("/db/betterform/apps/timetracker", $betterform, "*.xql","application/xquery"),
				setup:store-files("/db/betterform/apps/timetracker", $betterform, "*.html","text/html"),
				setup:store-files("/db/betterform/apps/timetracker", $betterform, "*.xml","application/xml"),
				setup:create-collection("/db/betterform/apps/timetracker", "data"),
				setup:store-files("/db/betterform/apps/timetracker/data", $betterform, "data/*.xml", "application/xml"),
				setup:create-collection("/db/betterform/apps/timetracker/data", "task"),
				setup:store-files("/db/betterform/apps/timetracker/data/task", $betterform, "data/task/*.xml","application/xml"),
				setup:create-collection("/db/betterform/apps/timetracker/", "edit"),
				setup:store-files("/db/betterform/apps/timetracker/edit", $betterform, "edit/*.xql","application/xquery"),
				setup:create-collection("/db/betterform/apps/timetracker/", "lucene"),
				setup:store-files("/db/betterform/apps/timetracker/lucene", $betterform, "lucene/*.xconf","application/xml"),
				setup:create-collection("/db/betterform/apps/timetracker/", "reports"),
				setup:store-files("/db/betterform/apps/timetracker/reports", $betterform, "reports/*.xql","application/xquery"),
				setup:create-collection("/db/betterform/apps/timetracker/", "resources"),
				setup:store-files("/db/betterform/apps/timetracker/resources", $betterform, "resources/*.css","text/css"),
				setup:create-collection("/db/betterform/apps/timetracker/resources", "images"),
				setup:store-files("/db/betterform/apps/timetracker/resources/images", $betterform, "resources/images/*.gif","image/gif"),
				setup:store-files("/db/betterform/apps/timetracker/resources/images", $betterform, "resources/images/*.png","image/png"),
				setup:store-files("/db/betterform/apps/timetracker/resources/images", $betterform, "resources/images/*.jpg","image/jpeg"),
				setup:create-collection("/db/betterform/apps/timetracker/", "search"),
				setup:store-files("/db/betterform/apps/timetracker/search", $betterform, "search/*.xql","application/xquery"),
				setup:store-files("/db/betterform/apps/timetracker/search", $betterform, "search/*.html","text/html"),
				setup:create-collection("/db/betterform/apps/timetracker/", "update"),
				setup:store-files("/db/betterform/apps/timetracker/update", $betterform, "update/*.xql","application/xquery"),
				setup:create-collection("/db/betterform/apps/timetracker/", "views"),
				setup:store-files("/db/betterform/apps/timetracker/views", $betterform, "views/*.xql","application/xquery"),
				setup:store-files("/db/betterform/apps/timetracker/views", $betterform, "views/*.html","text/html"),
				setup:store-files("/db/betterform/apps/timetracker/views", $betterform, "views/*.xsl","application/xml")
                :)
    )
};

declare function setup:importFromURLs() as element()+
{
    (
        setup:report(),

Actions:

            {
                let $includeXmlad := request:get-parameter("xmlad", ()),
                $includeMondial := request:get-parameter("mondial", ()),
 				$includeLocal := request:get-parameter("local", ()) return
                (
					if($includeLocal) then
					( setup:importLocal()
					)
					else (),
                    if($includeXmlad) then
                    (
                        setup:create-collection("/db", "xmlad"),
                        setup:load-URL("/db/xmlad", xs:anyURI("http://surfnet.dl.sourceforge.net/sourceforge/xmlad/xmlad.xml"), "xmlad.xml")
                    )else (),
                    if($includeMondial) then
                    (
                        setup:create-collection("/db", "mondial"),
                        setup:load-URL("/db/mondial", xs:anyURI("http://www.dbis.informatik.uni-goettingen.de/Mondial/mondial-europe.xml"), "mondial.xml")
                    )else ()
                )
            }

    )
};

declare function setup:load-URL($collection as xs:string, $url as xs:anyURI, $docName as xs:string) as element()
{
    let $x := xdb:store($collection, $docName, $url) return

File {$docName} imported from url: {$url}

};

declare function setup:store-files($collection as xs:string, $home as xs:string, $patterns as xs:string, $mimeType as xs:string) as element()*
{
    let $stored := xdb:store-files-from-pattern($collection, $home, $patterns, $mimeType)
    for $doc in $stored return

Uploaded: {$doc}

};

declare function setup:create-collection($parent as xs:string, $name as xs:string) as element()
{
    let $col := xdb:create-collection($parent, $name) return

Created collection: {$col}

};

declare function setup:create-group($group as xs:string) as element()
{
    if(xdb:group-exists($group))then
    (

Verified group: {$group} exists

    )
    else
    (
        let $result := xdb:create-group($group, xdb:get-current-user()) return
            if($result)then (

Created group: {$group}

            ) else (

Failed to create group: {$group}

            )
    )
};

declare function setup:set-collection-group($collection as xs:string, $group as xs:string) as element() {
    let $null := xdb:set-collection-permissions($collection, xdb:get-owner($collection), $group, xdb:get-permissions($collection)) return

Set collection '{$collection}' group to '{$group}'

};

declare function setup:set-collection-permissions($collection as xs:string, $permissions as xs:string) as element() {
        let $null := xdb:set-collection-permissions($collection, xdb:get-owner($collection), xdb:get-group($collection), xdb:string-to-permissions($permissions)) return

Set collection '{$collection}' permissions '{$permissions}'.

};

declare function setup:select() as element()
{
    <form action="{session:encode-url(request:get-uri())}" method="post">

eXist ships with a number of XQuery examples. Some of these
        require certain documents to be stored in the database. The XQuery examples also use some
		XML data not included with the distribution which can be downloaded by selecting
		the checkboxes below.

		<input type="checkbox" name="local" checked="true"/>
		eXist-db shipped files

        <input type="checkbox" name="xmlad"/>
        The XML Acronym Demystifier
        (approx. 384K)

        <input type="checkbox" name="mondial"/>
        The Mondial Database (Europe)
        (approx. 410K)

<input type="submit" name="action" value="Import Files"/>
        <input type="submit" name="action" value="Skip"/>

        <input type="hidden" name="panel" value="setup"/>
    </form>
};

declare function setup:report() as element()
{

Files have been loaded. You can now go to the
    examples page.

};

xquery version "1.0";
(: $Id$ :)
(:
    Module: examples setup
:)

module namespace setup="http://exist-db.org/xquery/admin-interface/setup";

declare namespace xdb="http://exist-db.org/xquery/xmldb";
declare namespace request="http://exist-db.org/xquery/request";
declare namespace session="http://exist-db.org/xquery/session";
declare namespace util="http://exist-db.org/xquery/util";
declare namespace system="http://exist-db.org/xquery/system";

declare namespace xproc="http://xproc.net/xproc";

declare function setup:main() as element()
{

Examples Setup

        {
let $action := request:get-parameter("action", ()) return
		if($action) then
		                (
		                    if($action eq "Import Example Data") then
		                    (
		                        setup:importLocal()
		                    )
		                    else if($action eq "Import Files") then
		                    (
		                        setup:importFromURLs()
		                    )
		                    else
		                    (
		                        setup:report()
		                    )
		                )
		                else
		                (
		                    setup:select()
		                )

        }

};

declare function setup:importLocal() as element()+
{
    let $home := system:get-exist-home(),
    $pathSep := util:system-property("file.separator"),
    $dir := if (doc-available(concat("file:///", $home, "/exist-samples/examples.xml"))) then
        (
            concat($home, $pathSep, "exist-samples")
        )
        else if(ends-with($home, "WEB-INF")) then
        (
            concat(substring-before($home, "WEB-INF"), "samples")
        )
        else
        (
            concat($home, $pathSep, "samples")
        ),
    $webapp := if (doc-available(concat("file:///", $home, "/webapp/index.xml"))) then
        (
            concat($home, $pathSep, "webapp")
        )
        else if(ends-with($home, "WEB-INF")) then
        (
            substring-before($home, "WEB-INF")
        )
        else
        (
            concat($home, $pathSep, "webapp")
        )
        (: ,
        $betterform := concat($home, "/extensions/betterform/src/main/xrx/timetracker/")
        :)
    return (
                setup:create-collection("/db/system/config", "db"),
                setup:create-collection("/db/system/config/db", "shakespeare"),
                setup:store-files("/db/system/config/db/shakespeare", $dir,
                    "shakespeare/collection.xconf", "text/xml"),
                setup:create-collection("/db", "shakespeare"),
                setup:create-collection("/db/shakespeare", "plays"),
                setup:store-files("/db/shakespeare/plays", $dir,  "shakespeare/*.xml", "text/xml"),
                setup:store-files("/db/shakespeare/plays", $dir, "shakespeare/*.xsl", "text/xml"),
                setup:store-files("/db/shakespeare/plays", $dir, "shakespeare/*.css", "text/css"),
                setup:create-collection("/db", "xinclude"),
                setup:create-collection("/db/xinclude", "styles"),
                setup:store-files("/db/xinclude", $dir, "xinclude/*.xsl", "text/xml"),
                setup:store-files("/db/xinclude", $dir, "xinclude/*.xml", "text/xml"),
                setup:store-files("/db/xinclude", $dir, "xinclude/*.xq", "application/xquery"),
                setup:store-files("/db/xinclude", $dir, "xinclude/*.jpg", "image/jpeg"),
                setup:store-files("/db/xinclude/styles", $dir, "xinclude/styles/*", "text/css"),
                setup:create-collection("/db/xinclude", "scripts"),
                setup:create-collection("/db/xinclude/scripts", "syntax"),
                setup:store-files("/db/xinclude/scripts/syntax", $dir, "xinclude/scripts/syntax/*.js", "application/x-javascript"),
                setup:create-collection("/db", "library"),
                setup:store-files("/db/library", $dir, "*.rdf", "text/xml"),
                setup:create-collection("/db/system/config/db", "mods"),
                setup:store-files("/db/system/config/db/mods", $dir, "mods/collection.xconf", "text/xml"),
                setup:create-collection("/db", "mods"),
                setup:create-collection("/db/mods", "eXist"),
                setup:create-collection("/db/mods", "samples"),
                setup:create-collection("/db/mods", "users"),
                setup:create-group("biblio.users"),
                setup:set-collection-group("/db/mods/users", "biblio.users"),
                setup:set-collection-permissions("/db/mods/users", "rwurwur--"),
                setup:create-collection("/db/mods", "groups"),
                setup:set-collection-group("/db/mods/groups", "biblio.users"),
                setup:set-collection-permissions("/db/mods/groups", "rwurwu---"),
                setup:store-files("/db/mods/samples", $dir, "mods/*.xml", "text/xml"),
                setup:store-files("/db/mods/eXist", $dir, "mods/eXist/*.xml", "text/xml"),
                setup:store-files("/db", $dir, "*.xml", "text/xml"),
                setup:create-collection("/db/system/config/db", "mondial"),
                setup:store-files("/db/system/config/db/mondial", $dir, "mondial.xconf", "text/xml"),
                setup:create-collection("/db/system/config/db", "xmlad"),
                setup:store-files("/db/system/config/db/xmlad", $dir, "acronyms.xconf", "text/xml"),

                (: Storing Testing :)
                setup:create-collection("/db", "testing"),
                setup:store-files("/db/testing", $webapp, "testing/*.xml", "text/html"),
                setup:store-files("/db/testing", $webapp, "testing/*.xq*", "application/xquery"),
                setup:create-collection("/db/testing", "examples"),
                setup:store-files("/db/testing/examples", $webapp, "testing/examples/*.xml", "text/html"),
                setup:store-files("/db/testing/examples", $webapp, "testing/examples/*.xq*", "application/xquery"),

                (: Storing XProc :)
                setup:create-collection("/db", "xproc"),
                setup:create-collection("/db/xproc", "unit-test"),
                setup:create-collection("/db/xproc", "xml"),
                setup:create-collection("/db/xproc", "xslt"),
                setup:create-collection("/db/xproc", "result"),
                setup:create-collection("/db/xproc", "examples"),
                setup:create-collection("/db/xproc", "pipelines"),
                setup:store-files("/db/xproc/unit-test", $webapp, "xproc/unit-test/*.xml", "text/xml"),
                setup:store-files("/db/xproc/unit-test", $webapp, "xproc/unit-test/*.xql", "application/xquery"),
                setup:store-files("/db/xproc/unit-test", $webapp, "xproc/unit-test/*.xsl", "text/xml"),

                setup:store-files("/db/xproc/pipelines", $webapp, "xproc/pipelines/*.xml", "text/xml"),
                setup:store-files("/db/xproc/xml", $webapp, "xproc/xml/*.xml", "text/xml"),
                setup:store-files("/db/xproc/xslt", $webapp, "xproc/xml/*.xsl", "text/xml"),

                setup:store-files("/db/xproc/examples", $webapp, "xproc/examples/*.xproc", "application/xml+xproc"),
                setup:store-files("/db/xproc/examples", $webapp, "xproc/examples/*.xml", "text/xml"),

                setup:store-files("/db/xproc/examples", $webapp, "xproc/examples/*.xql", "application/xquery"),
                setup:store-files("/db/xproc", $webapp, "xproc/*.xml", "text/xml"),
                setup:store-files("/db/xproc", $webapp, "xproc/*.xql", "application/xquery") (:,

                (: Storing XForms in the db won't work right now :)

                setup:create-collection("/db", "xforms"),
                setup:store-files("/db/xforms", $webapp, "xforms/*.xml", "text/html"),
                setup:store-files("/db/xforms", $webapp, "xforms/*.xq*", "application/xquery"),
                setup:create-collection("/db/xforms", "xsltforms"),
                setup:create-collection("/db/xforms", "data"),
                setup:store-files("/db/xforms/xsltforms", $webapp, "xforms/xsltforms/*.properties", "text/plain"),
                setup:store-files("/db/xforms/xsltforms", $webapp, "xforms/xsltforms/*.png", "image/png"),
                setup:store-files("/db/xforms/xsltforms", $webapp, "xforms/xsltforms/*.gif", "image/gif"),
                setup:store-files("/db/xforms/xsltforms", $webapp, "xforms/xsltforms/*.xsl","text/xml"),
                setup:store-files("/db/xforms/xsltforms", $webapp, "xforms/xsltforms/*.js","application/x-javascript"),
                setup:store-files("/db/xforms/xsltforms", $webapp, "xforms/xsltforms/*.css","text/css")
                :)

				(: Storing betterFORM XRX sample application :)
				(:
				setup:create-collection("/db", "betterform"),
				setup:create-collection("/db/betterform", "apps"),
				setup:create-collection("/db/betterform/apps", "timetracker"),
				setup:store-files("/db/betterform/apps/timetracker", $betterform, "*.xql","application/xquery"),
				setup:store-files("/db/betterform/apps/timetracker", $betterform, "*.html","text/html"),
				setup:store-files("/db/betterform/apps/timetracker", $betterform, "*.xml","application/xml"),
				setup:create-collection("/db/betterform/apps/timetracker", "data"),
				setup:store-files("/db/betterform/apps/timetracker/data", $betterform, "data/*.xml", "application/xml"),
				setup:create-collection("/db/betterform/apps/timetracker/data", "task"),
				setup:store-files("/db/betterform/apps/timetracker/data/task", $betterform, "data/task/*.xml","application/xml"),
				setup:create-collection("/db/betterform/apps/timetracker/", "edit"),
				setup:store-files("/db/betterform/apps/timetracker/edit", $betterform, "edit/*.xql","application/xquery"),
				setup:create-collection("/db/betterform/apps/timetracker/", "lucene"),
				setup:store-files("/db/betterform/apps/timetracker/lucene", $betterform, "lucene/*.xconf","application/xml"),
				setup:create-collection("/db/betterform/apps/timetracker/", "reports"),
				setup:store-files("/db/betterform/apps/timetracker/reports", $betterform, "reports/*.xql","application/xquery"),
				setup:create-collection("/db/betterform/apps/timetracker/", "resources"),
				setup:store-files("/db/betterform/apps/timetracker/resources", $betterform, "resources/*.css","text/css"),
				setup:create-collection("/db/betterform/apps/timetracker/resources", "images"),
				setup:store-files("/db/betterform/apps/timetracker/resources/images", $betterform, "resources/images/*.gif","image/gif"),
				setup:store-files("/db/betterform/apps/timetracker/resources/images", $betterform, "resources/images/*.png","image/png"),
				setup:store-files("/db/betterform/apps/timetracker/resources/images", $betterform, "resources/images/*.jpg","image/jpeg"),
				setup:create-collection("/db/betterform/apps/timetracker/", "search"),
				setup:store-files("/db/betterform/apps/timetracker/search", $betterform, "search/*.xql","application/xquery"),
				setup:store-files("/db/betterform/apps/timetracker/search", $betterform, "search/*.html","text/html"),
				setup:create-collection("/db/betterform/apps/timetracker/", "update"),
				setup:store-files("/db/betterform/apps/timetracker/update", $betterform, "update/*.xql","application/xquery"),
				setup:create-collection("/db/betterform/apps/timetracker/", "views"),
				setup:store-files("/db/betterform/apps/timetracker/views", $betterform, "views/*.xql","application/xquery"),
				setup:store-files("/db/betterform/apps/timetracker/views", $betterform, "views/*.html","text/html"),
				setup:store-files("/db/betterform/apps/timetracker/views", $betterform, "views/*.xsl","application/xml")
                :)
    )
};

declare function setup:importFromURLs() as element()+
{
    (
        setup:report(),

Actions:

            {
                let $includeXmlad := request:get-parameter("xmlad", ()),
                $includeMondial := request:get-parameter("mondial", ()),
 				$includeLocal := request:get-parameter("local", ()) return
                (
					if($includeLocal) then
					( setup:importLocal()
					)
					else (),
                    if($includeXmlad) then
                    (
                        setup:create-collection("/db", "xmlad"),
                        setup:load-URL("/db/xmlad", xs:anyURI("http://surfnet.dl.sourceforge.net/sourceforge/xmlad/xmlad.xml"), "xmlad.xml")
                    )else (),
                    if($includeMondial) then
                    (
                        setup:create-collection("/db", "mondial"),
                        setup:load-URL("/db/mondial", xs:anyURI("http://www.dbis.informatik.uni-goettingen.de/Mondial/mondial-europe.xml"), "mondial.xml")
                    )else ()
                )
            }

    )
};

declare function setup:load-URL($collection as xs:string, $url as xs:anyURI, $docName as xs:string) as element()
{
    let $x := xdb:store($collection, $docName, $url) return

File {$docName} imported from url: {$url}

};

declare function setup:store-files($collection as xs:string, $home as xs:string, $patterns as xs:string, $mimeType as xs:string) as element()*
{
    let $stored := xdb:store-files-from-pattern($collection, $home, $patterns, $mimeType)
    for $doc in $stored return

Uploaded: {$doc}

};

declare function setup:create-collection($parent as xs:string, $name as xs:string) as element()
{
    let $col := xdb:create-collection($parent, $name) return

Created collection: {$col}

};

declare function setup:create-group($group as xs:string) as element()
{
    let $result := xdb:create-group($group, xdb:get-current-user()) return
        if($result)then (

Created group: {$group}

        ) else (

Failed to create group: {$group}

        )
};

declare function setup:set-collection-group($collection as xs:string, $group as xs:string) as element() {
    let $null := xdb:set-collection-permissions($collection, xdb:get-owner($collection), $group, xdb:get-permissions($collection)) return

Set collection '{$collection}' group to '{$group}'

};

declare function setup:set-collection-permissions($collection as xs:string, $permissions as xs:string) as element() {
        let $null := xdb:set-collection-permissions($collection, xdb:get-owner($collection), xdb:get-group($collection), xdb:string-to-permissions($permissions)) return

Set collection '{$collection}' permissions '{$permissions}'.

};

declare function setup:select() as element()
{
    <form action="{session:encode-url(request:get-uri())}" method="post">

eXist ships with a number of XQuery examples. Some of these
        require certain documents to be stored in the database. The XQuery examples also use some
		XML data not included with the distribution which can be downloaded by selecting
		the checkboxes below.

		<input type="checkbox" name="local" checked="true"/>
		eXist-db shipped files

        <input type="checkbox" name="xmlad"/>
        The XML Acronym Demystifier
        (approx. 384K)

        <input type="checkbox" name="mondial"/>
        The Mondial Database (Europe)
        (approx. 410K)

<input type="submit" name="action" value="Import Files"/>
        <input type="submit" name="action" value="Skip"/>

        <input type="hidden" name="panel" value="setup"/>
    </form>
};

declare function setup:report() as element()
{

Files have been loaded. You can now go to the
    examples page.

};