DHFPROD-3470: update dhf4 flow unit test with proper camelcase instead of colon

xquery version "1.0-ml";

module namespace tf = "http://example.com/test-flows-lib";

import module namespace cfg = "http://example.com/config" at "/lib/config.xqy";
import module namespace tcfg = "http://example.com/test-config" at "/lib/test-config.xqy";
import module namespace functx = "http://www.functx.com" at "/MarkLogic/functx/functx-1.0-nodoc-2007-01.xqy";
import module namespace th="http://marklogic.com/test/unit" at "/test/test-helper.xqy";

declare namespace http="xdmp:http";
declare namespace hub = "http://marklogic.com/data-hub";
declare namespace es = "http://marklogic.com/entity-services";

declare variable $FLOW-URL as xs:string :=  "/v1/resources/mlFlow?";
declare variable $RUN-COLLECTOR-URL as xs:string :=  "/v1/internal/hubcollector?";

declare variable $HTTP-OK as xs:integer := 200;

declare variable $HARMONIZE-FLOW-TYPE as xs:string := "harmonize";
declare variable $INPUT-FLOW-TYPE as xs:string := "input";

(:make sure Accept header is set to control how exceptions are returned to caller:)
declare variable $HTTP-OPTIONS as element(http:options) :=
    <options xmlns="xdmp:http">
        <authentication method="digest">
            <username>{$cfg:DATAHUB-USER}</username>
            <password>{$cfg:DATAHUB-USER-PWD}</password>
        </authentication>
        <headers>
            <Accept>application/xml</Accept>
            <Content-Type>application/xml</Content-Type>
        </headers>
    </options>;

declare function remove-test-content-docs()
{
    let $collection-query := cts:collection-query($tcfg:UNIT-TEST-COLLECTION)
    let $_ :=
        xdmp:invoke-function(
            function() {
                xdmp:log("XXX removing "|| fn:count( cts:uri-match("/content/*", (), $collection-query) )  ||  " unit test content docs from STAGING"   ),

                cts:uri-match("/content/*", (), $collection-query) => xdmp:document-delete()
            },
            <options xmlns="xdmp:eval">
                <isolation>different-transaction</isolation>
                <database>{xdmp:database($cfg:STAGING-DB)}</database>
                <transaction-mode>update-auto-commit</transaction-mode>
            </options>

        )
    return xdmp:invoke-function(
            function() {
                xdmp:log("XXX number of unit test content docs remaining in STAGING = "||fn:count( cts:uri-match("/content/*", (), $collection-query) ) )
            },
            <options xmlns="xdmp:eval">
                <isolation>different-transaction</isolation>
                <database>{xdmp:database($cfg:STAGING-DB)}</database>
            </options>

    )

};

(:~
: example: run an input flow
:)
declare function run-input-flow-XXX(
        $host as xs:string,
        $port as xs:string
)
{
    let $flow-name := "XXX"
    let $get-foo-docs := function() as map:map {
        let $results := map:map()
        let $_ := for $source-uri in xdmp:invoke-function(
                        (:load content from a test folder:)
                        function() {cts:uri-match( "/test/content/FOO/*")},
                        <options xmlns="xdmp:eval">
                            <database>{xdmp:modules-database()}</database>
                        </options>
                )
                let $doc := th:get-modules-file($source-uri, "xml" )
                let $uri := fn:concat("/content/raw/FOO/", tf:get-root-filename($source-uri), ".xml")
                return  map:put($results, $uri, $doc)
        return $results

    }
    let $entity-name := "FOOEntity"
    return run-input-flow($host,$port,$entity-name,$flow-name, $get-foo-docs)
};

declare function run-input-flow(
        $host as xs:string,
        $port as xs:string,
        $entity-name as xs:string,
        $flow-name as xs:string,
        $get-docs as function() as map:map
)
{
    let $doc-map as map:map := $get-docs()
    for $uri in map:keys($doc-map)
    let $job-id := "foo-manual-process-test-1"
    let $input-collections := ( $entity-name, $flow-name,  $INPUT-FLOW-TYPE, $tcfg:UNIT-TEST-COLLECTION) => fn:string-join(",")
    let $options := let $map := map:map()
    let $_ := $map  => map:with($cfg:INPUT-COLLECTIONS-KEY, $input-collections)
                        => map:with('entity', $entity-name)
                        => map:with('flow', $flow-name)
                        => map:with('flowType', $INPUT-FLOW-TYPE)
                        return xdmp:to-json-string($map) => xdmp:url-encode()
    let $url := make-input-flow-url($host, $port, $uri, $entity-name, $flow-name, $input-collections, $job-id, $options )
    return tcfg:run-http($tcfg:HTTP-PUT, $url, tcfg:set-PUT-http-options(element payload { map:get($doc-map,$uri) }) )
};

declare function make-input-flow-url(
        $host as xs:string,
        $port as xs:string,
        $uri as xs:string,
        $entity-name as xs:string,
        $flow-name as xs:string,
        $collection-string as xs:string?,
        $job-id as xs:string?,
        $options as xs:string?
)
{
    let $collection-parms := fn:tokenize($collection-string, ",") ! fn:concat("&collection=",functx:trim(.))  => fn:string-join()
    return
        fn:concat("http://",$host,":",$port,"/v1/documents?uri=",
            $uri,
            "&transform=mlInputFlow",
            "&trans:flow-name=",
            $flow-name,
            "&trans:entity-name=",
            $entity-name,
            $collection-parms,
            if ($job-id) then "&trans:job-id="||$job-id else (),
            if ($options) then "&trans:options="||$options else ()

    )
};
declare function get-root-filename(
        $file-path as xs:string
) as xs:string
{
    fn:substring-before($file-path, ".") => functx:substring-after-last-match("/")
};

(:~

?rs:identifiers=someIdentifier&rs:entity-name=EntityName&rs:flow-name=FlowName&rs:target-database=Documents&rs:options={"some":"json"}&rs:job-id=SomeJobID

 -PentityName=$ENTITY \
  -PflowName=$FLOW \
  -PbatchSize=10 \
  -PthreadCount=4 \
  -PsourceDB=data-hub-STAGING \
  -PdestDB=data-hub-FINAL \
  -PshowOptions=true \
  -PenvironmentName=$ENV \
  -Pdhf.inputCollections=$COLLS
:)
declare function tf:build-flow-url(
    $ids as xs:string*,
    $entity as xs:string,
    $flow-name as xs:string,
    $target-db as xs:string,
    $source-db as xs:string,
    $options as xs:string,
    $job-id as xs:string

) as xs:string
{
  fn:concat(
           if ($job-id) then fn:concat("rs:job-id=",$job-id ) else (),
           if ($flow-name) then fn:concat("&rs:flow-name=",$flow-name ) else (),
           if ($target-db) then fn:concat("&rs:target-database=",$target-db ) else (),
           if ($options) then fn:concat("&rs:options=",$options ) else (),
           if ($entity) then fn:concat("&rs:entity-name=",$entity ) else (),
           if ($source-db) then fn:concat("&database=",$source-db ) else (),
           fn:concat("&rs:identifiers=",fn:string-join($ids,"&rs:identifiers=" ) )

  )
};

declare function tf:exec-http(
        $http-func as function(item(), item()) as item()+,
        $url as xs:string,
        $options as element(http:options)?
) as element(response)
{
    let $response := $http-func($url,if ($options) then $options else $HTTP-OPTIONS)
    let $_ := xdmp:log(" http response[1] " ||xdmp:quote($response[1]) )
    let $_ := xdmp:log(" http response[2] " ||xdmp:quote($response[2]) )
    return <response><part1>{$response[1]}</part1><part2>{if (fn:count($response[2]/(element()|text())) eq 0) then () else $response[2]}</part2></response>
};

(:~

:)
(:~
: get the xml representation of a harmonisation flow.
:
: based on the following http commands extracted from the MarkLogic Access Log
:
: @param url  url of the custom resource flow endpoint
: @param entity  name of the entity
: @param flow-name name of the flow
: @param db the source db e.g. STAGING
: @return  response xml
:)

declare function tf:get-harmonisation-flow(
        $url as xs:string,
        $entity as xs:string,
        $flow-name as xs:string,
        $db as xs:string
) as element(response)
{

    tf:exec-http($tcfg:HTTP-GET,
                    fn:concat(
                            $url,
                        "rs:flow-name=",
                        $flow-name,
                        "&rs:flow-type=",
                        $HARMONIZE-FLOW-TYPE,
                        "&rs:entity-name=",
                        $entity,
                        "&database=",
                        $db

                    ),
                    ()
    )

};

(:~
: run the collector of a given harmonisation flow.
:
: based on the following http commands extracted from the MarkLogic Access Log
:
: @param url  url of the custom resource flow endpoint
: @param job-id  job id
: @param entity-name  name of the entity
: @param flow-name name of the flow
: @param options  will be passed to the xquery code
: @param db the source db e.g. STAGING
: @return  response xml
:)
declare function tf:run-collector(
        $url as xs:string,
        $job-id as xs:string,
        $entity-name as xs:string,
        $flow-name as xs:string,
        $options as xs:string,
        $db as xs:string
) as element(response)
{

    tf:exec-http($tcfg:HTTP-GET,
            fn:concat(
                    $url,
                    "job-id=",
                    $job-id,
                    "&entity-name=",
                    $entity-name,
                    "&flow-name=",
                    $flow-name,
                    "&database=",
                    $db,
                    "&options=",
                    $options
            ),
            ()
    )

};

(:~
: run  the main part of a harmonisation flow i.e. the bit without the collector
:
:
: based on the following http commands extracted from the MarkLogic Access Log
: POST batches of URIs to a flow for processing
:
: @param url  url of the custom resource flow endpoint
: @param ids  urls from staging of the documents to be written
: @param job-id  job id
: @param entity-name  name of the entity
: @param flow-name name of the flow
: @param target-db  the target db where the docs will be writting
: @param options  will be passed to the xquery code
: @param flow  the flow xml as element(hub:flow),
: @param db the source db e.g. STAGING
: @return  response xml
:)
declare function tf:run-flow
(
        $url as xs:string,
        $ids as xs:string*,
        $job-id as xs:string,
        $entity-name as xs:string,
        $flow-name as xs:string,
        $target-db as xs:string,
        $options as xs:string,
        $flow as element(hub:flow),
        $db as xs:string
) as element(response)
{
    xdmp:log("XXX url="||tf:build-flow-url($ids, $entity-name, $flow-name, $target-db , $db, $options, $job-id) ),
    tf:exec-http($tcfg:HTTP-POST,
                    fn:concat(
                            $url,
                            tf:build-flow-url($ids, $entity-name, $flow-name, $target-db , $db, $options, $job-id)
                    ),
                     <options xmlns="xdmp:http">
                        <authentication method="digest">
                            <username>{$cfg:DATAHUB-USER}</username>
                            <password>{$cfg:DATAHUB-USER-PWD}</password>
                        </authentication>
                        <headers>
                            <Accept>application/xml</Accept>
                            <Content-Type>application/xml</Content-Type>
                        </headers>
                        <data>{xdmp:quote($flow)}</data>
                    </options>
    )

};
(:~
: run an entire harmonisation flow (all components)
:
: @param entity-name  name of the entity
: @param flow-name name of the flow
: @param source-db the source db e.g. STAGING
: @param target-db  the target db where the docs will be writting
: @param host  the hostname where the DHF is running
: @param port  the port on which the DHF is listening
: @param input-collections  a comma separated string with collection names to be included in the initial selection of
:                           documents in the collector
: @param input-pub-year the publication year to be used in the collector, must 4 digit e.g. 2017
: @param input-error-reprocessing whether to reprocess error documents from the TRACING db
:
: @return  response xml
:
:)
declare function tf:run-harmonisation-flow(
    $entity as xs:string,
    $flow-name as xs:string,
    $source-db as xs:string,
    $target-db as xs:string,
    $host as xs:string,
    $port as xs:string,
    $input-collections as xs:string?,
    $begin-pub-year as xs:string?,
    $end-pub-year as xs:string?,
    $input-error-reprocessing as xs:boolean?,
    $invoke-termite as xs:boolean?
) as item()?
{
    let $throw-harmonization-error := function($message as xs:string) {
        fn:error(xs:QName("HARMONIZATION-ERROR"), $message)
    }
    let $base-url := "http://" || $host || ":" || $port
    let $flow as element(hub:flow)?  := tf:get-harmonisation-flow($base-url || $FLOW-URL, $entity, $flow-name, $source-db)
    ! (if ($HTTP-OK ne ./part1/http:response/http:code/fn:data()) then
            $throw-harmonization-error(" GET Flow http status was "||./part1/http:response/http:code/fn:string() )
        else ./part2/hub:flow)

    let $job-id := sem:uuid-string()
    let $options := let $map := map:map()
                    let $_ := $map => map:with($cfg:INPUT-COLLECTIONS-KEY, $input-collections)
                                    => map:with('entity', $entity)
                                    => map:with('flow', $flow-name)
                                    => map:with('flowType', $HARMONIZE-FLOW-TYPE)
                    return xdmp:to-json-string($map)  => xdmp:url-encode()
    let $_ := xdmp:log("XX running collector")
    let $uris := tf:run-collector($base-url || $RUN-COLLECTOR-URL, $job-id, $entity, $flow-name, $options, $source-db)
        ! (if ($HTTP-OK ne ./part1/http:response/http:code/fn:data()) then
            $throw-harmonization-error(" Run Collector http status was "||./part1/http:response/http:code/fn:string() )
           else ./part2 => fn:tokenize("\n")  )

    let $_ :=  xdmp:log("URIS = "||fn:count($uris) )

    let $_ := xdmp:log("XX running flow")
    return if ($uris) then tf:run-flow($base-url || $FLOW-URL, $uris, $job-id, $entity, $flow-name, $target-db, $options,  $flow, $source-db)
    ! (if ($HTTP-OK ne ./part1/http:response/http:code/fn:data()) then
        $throw-harmonization-error(" Run Flow http status was "||./part1/http:response/http:code/fn:string() )
       else ./part2)
    else xdmp:log("found no uris to process")
};
(:~
: run an entire harmonisation flow (all components)
:
: @param entity-name  name of the entity
: @param flow-name name of the flow
: @param source-db the source db e.g. STAGING
: @param target-db  the target db where the docs will be writting
: @param host  the hostname where the DHF is running
: @param port  the port on which the DHF is listening
: @param preformatted harmonisation options as xs:string
:
: @return  response xml
:
:)
declare function tf:run-harmonisation-flow-with-options(
    $entity as xs:string,
    $flow-name as xs:string,
    $source-db as xs:string,
    $target-db as xs:string,
    $host as xs:string,
    $port as xs:string,
    $dhf-options as xs:string
) as item()?
{
    let $throw-harmonization-error := function($message as xs:string) {
        fn:error(xs:QName("HARMONIZATION-ERROR"), $message)
    }
    let $base-url := "http://" || $host || ":" || $port
    let $flow as element(hub:flow)?  := tf:get-harmonisation-flow($base-url || $FLOW-URL, $entity, $flow-name, $source-db)
    ! (if ($HTTP-OK ne ./part1/http:response/http:code/fn:data()) then
            $throw-harmonization-error(" GET Flow http status was "||./part1/http:response/http:code/fn:string() )
        else ./part2/hub:flow)

    let $job-id := sem:uuid-string()
    let $options := $dhf-options  => xdmp:url-encode()
    let $_ := xdmp:log("XX running collector")
    let $uris := tf:run-collector($base-url || $RUN-COLLECTOR-URL, $job-id, $entity, $flow-name, $options, $source-db)
        ! (if ($HTTP-OK ne ./part1/http:response/http:code/fn:data()) then
            $throw-harmonization-error(" Run Collector http status was "||./part1/http:response/http:code/fn:string() )
           else ./part2 => fn:tokenize("\n")  )

    let $_ :=  xdmp:log("URIS = "||fn:count($uris) )

    let $_ := xdmp:log("XX running flow")
    return if ($uris) then tf:run-flow($base-url || $FLOW-URL, $uris, $job-id, $entity, $flow-name, $target-db, $options,  $flow, $source-db)
    ! (if ($HTTP-OK ne ./part1/http:response/http:code/fn:data()) then
        $throw-harmonization-error(" Run Flow http status was "||./part1/http:response/http:code/fn:string() )
       else ./part2)
    else xdmp:log("found no uris to process")
};

declare private function make-options(
    $entity as xs:string,
    $flow-name as xs:string
) as xs:string
{
    map:map() => map:with('entity', $entity)
              => map:with('flow', $flow-name)
              => map:with('flowType', $HARMONIZE-FLOW-TYPE)
              => map:with($cfg:INPUT-COLLECTIONS-KEY, $tcfg:UNIT-TEST-COLLECTION)
              => xdmp:to-json-string()
};

declare function run-harmonize-employees()
{
    let $host := $tcfg:TEST-HOST
    let $port := $tcfg:TEST-STAGING-PORT
    let $flow-name := "harmonizeEmployees"
    let $db := $cfg:STAGING-DB
    let $target-db := $cfg:FINAL-DB
    let $entity-name := "Employee"
    let $options := make-options($entity-name, $flow-name)
    return  tf:run-harmonisation-flow-with-options($entity-name, $flow-name, $db, $target-db, $host, $port, $options)
};

xquery version "1.0-ml";

module namespace tf = "http://example.com/test-flows-lib";

import module namespace cfg = "http://example.com/config" at "/lib/config.xqy";
import module namespace tcfg = "http://example.com/test-config" at "/lib/test-config.xqy";
import module namespace functx = "http://www.functx.com" at "/MarkLogic/functx/functx-1.0-nodoc-2007-01.xqy";
import module namespace th="http://marklogic.com/test/unit" at "/test/test-helper.xqy";

declare namespace http="xdmp:http";
declare namespace hub = "http://marklogic.com/data-hub";
declare namespace es = "http://marklogic.com/entity-services";

declare variable $FLOW-URL as xs:string :=  "/v1/resources/ml:flow?";
declare variable $RUN-COLLECTOR-URL as xs:string :=  "/v1/internal/hubcollector?";

declare variable $HTTP-OK as xs:integer := 200;

declare variable $HARMONIZE-FLOW-TYPE as xs:string := "harmonize";
declare variable $INPUT-FLOW-TYPE as xs:string := "input";

(:make sure Accept header is set to control how exceptions are returned to caller:)
declare variable $HTTP-OPTIONS as element(http:options) :=
    <options xmlns="xdmp:http">
        <authentication method="digest">
            <username>{$cfg:DATAHUB-USER}</username>
            <password>{$cfg:DATAHUB-USER-PWD}</password>
        </authentication>
        <headers>
            <Accept>application/xml</Accept>
            <Content-Type>application/xml</Content-Type>
        </headers>
    </options>;

declare function remove-test-content-docs()
{
    let $collection-query := cts:collection-query($tcfg:UNIT-TEST-COLLECTION)
    let $_ :=
        xdmp:invoke-function(
            function() {
                xdmp:log("XXX removing "|| fn:count( cts:uri-match("/content/*", (), $collection-query) )  ||  " unit test content docs from STAGING"   ),

                cts:uri-match("/content/*", (), $collection-query) => xdmp:document-delete()
            },
            <options xmlns="xdmp:eval">
                <isolation>different-transaction</isolation>
                <database>{xdmp:database($cfg:STAGING-DB)}</database>
                <transaction-mode>update-auto-commit</transaction-mode>
            </options>

        )
    return xdmp:invoke-function(
            function() {
                xdmp:log("XXX number of unit test content docs remaining in STAGING = "||fn:count( cts:uri-match("/content/*", (), $collection-query) ) )
            },
            <options xmlns="xdmp:eval">
                <isolation>different-transaction</isolation>
                <database>{xdmp:database($cfg:STAGING-DB)}</database>
            </options>

    )

};

(:~
: example: run an input flow
:)
declare function run-input-flow-XXX(
        $host as xs:string,
        $port as xs:string
)
{
    let $flow-name := "XXX"
    let $get-foo-docs := function() as map:map {
        let $results := map:map()
        let $_ := for $source-uri in xdmp:invoke-function(
                        (:load content from a test folder:)
                        function() {cts:uri-match( "/test/content/FOO/*")},
                        <options xmlns="xdmp:eval">
                            <database>{xdmp:modules-database()}</database>
                        </options>
                )
                let $doc := th:get-modules-file($source-uri, "xml" )
                let $uri := fn:concat("/content/raw/FOO/", tf:get-root-filename($source-uri), ".xml")
                return  map:put($results, $uri, $doc)
        return $results

    }
    let $entity-name := "FOOEntity"
    return run-input-flow($host,$port,$entity-name,$flow-name, $get-foo-docs)
};

declare function run-input-flow(
        $host as xs:string,
        $port as xs:string,
        $entity-name as xs:string,
        $flow-name as xs:string,
        $get-docs as function() as map:map
)
{
    let $doc-map as map:map := $get-docs()
    for $uri in map:keys($doc-map)
    let $job-id := "foo-manual-process-test-1"
    let $input-collections := ( $entity-name, $flow-name,  $INPUT-FLOW-TYPE, $tcfg:UNIT-TEST-COLLECTION) => fn:string-join(",")
    let $options := let $map := map:map()
    let $_ := $map  => map:with($cfg:INPUT-COLLECTIONS-KEY, $input-collections)
                        => map:with('entity', $entity-name)
                        => map:with('flow', $flow-name)
                        => map:with('flowType', $INPUT-FLOW-TYPE)
                        return xdmp:to-json-string($map) => xdmp:url-encode()
    let $url := make-input-flow-url($host, $port, $uri, $entity-name, $flow-name, $input-collections, $job-id, $options )
    return tcfg:run-http($tcfg:HTTP-PUT, $url, tcfg:set-PUT-http-options(element payload { map:get($doc-map,$uri) }) )
};

declare function make-input-flow-url(
        $host as xs:string,
        $port as xs:string,
        $uri as xs:string,
        $entity-name as xs:string,
        $flow-name as xs:string,
        $collection-string as xs:string?,
        $job-id as xs:string?,
        $options as xs:string?
)
{
    let $collection-parms := fn:tokenize($collection-string, ",") ! fn:concat("&collection=",functx:trim(.))  => fn:string-join()
    return
        fn:concat("http://",$host,":",$port,"/v1/documents?uri=",
            $uri,
            "&transform=ml:inputFlow",
            "&trans:flow-name=",
            $flow-name,
            "&trans:entity-name=",
            $entity-name,
            $collection-parms,
            if ($job-id) then "&trans:job-id="||$job-id else (),
            if ($options) then "&trans:options="||$options else ()

    )
};
declare function get-root-filename(
        $file-path as xs:string
) as xs:string
{
    fn:substring-before($file-path, ".") => functx:substring-after-last-match("/")
};

(:~

?rs:identifiers=someIdentifier&rs:entity-name=EntityName&rs:flow-name=FlowName&rs:target-database=Documents&rs:options={"some":"json"}&rs:job-id=SomeJobID

 -PentityName=$ENTITY \
  -PflowName=$FLOW \
  -PbatchSize=10 \
  -PthreadCount=4 \
  -PsourceDB=data-hub-STAGING \
  -PdestDB=data-hub-FINAL \
  -PshowOptions=true \
  -PenvironmentName=$ENV \
  -Pdhf.inputCollections=$COLLS
:)
declare function tf:build-flow-url(
    $ids as xs:string*,
    $entity as xs:string,
    $flow-name as xs:string,
    $target-db as xs:string,
    $source-db as xs:string,
    $options as xs:string,
    $job-id as xs:string

) as xs:string
{
  fn:concat(
           if ($job-id) then fn:concat("rs:job-id=",$job-id ) else (),
           if ($flow-name) then fn:concat("&rs:flow-name=",$flow-name ) else (),
           if ($target-db) then fn:concat("&rs:target-database=",$target-db ) else (),
           if ($options) then fn:concat("&rs:options=",$options ) else (),
           if ($entity) then fn:concat("&rs:entity-name=",$entity ) else (),
           if ($source-db) then fn:concat("&database=",$source-db ) else (),
           fn:concat("&rs:identifiers=",fn:string-join($ids,"&rs:identifiers=" ) )

  )
};

declare function tf:exec-http(
        $http-func as function(item(), item()) as item()+,
        $url as xs:string,
        $options as element(http:options)?
) as element(response)
{
    let $response := $http-func($url,if ($options) then $options else $HTTP-OPTIONS)
    let $_ := xdmp:log(" http response[1] " ||xdmp:quote($response[1]) )
    let $_ := xdmp:log(" http response[2] " ||xdmp:quote($response[2]) )
    return <response><part1>{$response[1]}</part1><part2>{if (fn:count($response[2]/(element()|text())) eq 0) then () else $response[2]}</part2></response>
};

(:~

:)
(:~
: get the xml representation of a harmonisation flow.
:
: based on the following http commands extracted from the MarkLogic Access Log
:
: @param url  url of the custom resource flow endpoint
: @param entity  name of the entity
: @param flow-name name of the flow
: @param db the source db e.g. STAGING
: @return  response xml
:)

declare function tf:get-harmonisation-flow(
        $url as xs:string,
        $entity as xs:string,
        $flow-name as xs:string,
        $db as xs:string
) as element(response)
{

    tf:exec-http($tcfg:HTTP-GET,
                    fn:concat(
                            $url,
                        "rs:flow-name=",
                        $flow-name,
                        "&rs:flow-type=",
                        $HARMONIZE-FLOW-TYPE,
                        "&rs:entity-name=",
                        $entity,
                        "&database=",
                        $db

                    ),
                    ()
    )

};

(:~
: run the collector of a given harmonisation flow.
:
: based on the following http commands extracted from the MarkLogic Access Log
:
: @param url  url of the custom resource flow endpoint
: @param job-id  job id
: @param entity-name  name of the entity
: @param flow-name name of the flow
: @param options  will be passed to the xquery code
: @param db the source db e.g. STAGING
: @return  response xml
:)
declare function tf:run-collector(
        $url as xs:string,
        $job-id as xs:string,
        $entity-name as xs:string,
        $flow-name as xs:string,
        $options as xs:string,
        $db as xs:string
) as element(response)
{

    tf:exec-http($tcfg:HTTP-GET,
            fn:concat(
                    $url,
                    "job-id=",
                    $job-id,
                    "&entity-name=",
                    $entity-name,
                    "&flow-name=",
                    $flow-name,
                    "&database=",
                    $db,
                    "&options=",
                    $options
            ),
            ()
    )

};

(:~
: run  the main part of a harmonisation flow i.e. the bit without the collector
:
:
: based on the following http commands extracted from the MarkLogic Access Log
: POST batches of URIs to a flow for processing
:
: @param url  url of the custom resource flow endpoint
: @param ids  urls from staging of the documents to be written
: @param job-id  job id
: @param entity-name  name of the entity
: @param flow-name name of the flow
: @param target-db  the target db where the docs will be writting
: @param options  will be passed to the xquery code
: @param flow  the flow xml as element(hub:flow),
: @param db the source db e.g. STAGING
: @return  response xml
:)
declare function tf:run-flow
(
        $url as xs:string,
        $ids as xs:string*,
        $job-id as xs:string,
        $entity-name as xs:string,
        $flow-name as xs:string,
        $target-db as xs:string,
        $options as xs:string,
        $flow as element(hub:flow),
        $db as xs:string
) as element(response)
{
    xdmp:log("XXX url="||tf:build-flow-url($ids, $entity-name, $flow-name, $target-db , $db, $options, $job-id) ),
    tf:exec-http($tcfg:HTTP-POST,
                    fn:concat(
                            $url,
                            tf:build-flow-url($ids, $entity-name, $flow-name, $target-db , $db, $options, $job-id)
                    ),
                     <options xmlns="xdmp:http">
                        <authentication method="digest">
                            <username>{$cfg:DATAHUB-USER}</username>
                            <password>{$cfg:DATAHUB-USER-PWD}</password>
                        </authentication>
                        <headers>
                            <Accept>application/xml</Accept>
                            <Content-Type>application/xml</Content-Type>
                        </headers>
                        <data>{xdmp:quote($flow)}</data>
                    </options>
    )

};
(:~
: run an entire harmonisation flow (all components)
:
: @param entity-name  name of the entity
: @param flow-name name of the flow
: @param source-db the source db e.g. STAGING
: @param target-db  the target db where the docs will be writting
: @param host  the hostname where the DHF is running
: @param port  the port on which the DHF is listening
: @param input-collections  a comma separated string with collection names to be included in the initial selection of
:                           documents in the collector
: @param input-pub-year the publication year to be used in the collector, must 4 digit e.g. 2017
: @param input-error-reprocessing whether to reprocess error documents from the TRACING db
:
: @return  response xml
:
:)
declare function tf:run-harmonisation-flow(
    $entity as xs:string,
    $flow-name as xs:string,
    $source-db as xs:string,
    $target-db as xs:string,
    $host as xs:string,
    $port as xs:string,
    $input-collections as xs:string?,
    $begin-pub-year as xs:string?,
    $end-pub-year as xs:string?,
    $input-error-reprocessing as xs:boolean?,
    $invoke-termite as xs:boolean?
) as item()?
{
    let $throw-harmonization-error := function($message as xs:string) {
        fn:error(xs:QName("HARMONIZATION-ERROR"), $message)
    }
    let $base-url := "http://" || $host || ":" || $port
    let $flow as element(hub:flow)?  := tf:get-harmonisation-flow($base-url || $FLOW-URL, $entity, $flow-name, $source-db)
    ! (if ($HTTP-OK ne ./part1/http:response/http:code/fn:data()) then
            $throw-harmonization-error(" GET Flow http status was "||./part1/http:response/http:code/fn:string() )
        else ./part2/hub:flow)

    let $job-id := sem:uuid-string()
    let $options := let $map := map:map()
                    let $_ := $map => map:with($cfg:INPUT-COLLECTIONS-KEY, $input-collections)
                                    => map:with('entity', $entity)
                                    => map:with('flow', $flow-name)
                                    => map:with('flowType', $HARMONIZE-FLOW-TYPE)
                    return xdmp:to-json-string($map)  => xdmp:url-encode()
    let $_ := xdmp:log("XX running collector")
    let $uris := tf:run-collector($base-url || $RUN-COLLECTOR-URL, $job-id, $entity, $flow-name, $options, $source-db)
        ! (if ($HTTP-OK ne ./part1/http:response/http:code/fn:data()) then
            $throw-harmonization-error(" Run Collector http status was "||./part1/http:response/http:code/fn:string() )
           else ./part2 => fn:tokenize("\n")  )

    let $_ :=  xdmp:log("URIS = "||fn:count($uris) )

    let $_ := xdmp:log("XX running flow")
    return if ($uris) then tf:run-flow($base-url || $FLOW-URL, $uris, $job-id, $entity, $flow-name, $target-db, $options,  $flow, $source-db)
    ! (if ($HTTP-OK ne ./part1/http:response/http:code/fn:data()) then
        $throw-harmonization-error(" Run Flow http status was "||./part1/http:response/http:code/fn:string() )
       else ./part2)
    else xdmp:log("found no uris to process")
};
(:~
: run an entire harmonisation flow (all components)
:
: @param entity-name  name of the entity
: @param flow-name name of the flow
: @param source-db the source db e.g. STAGING
: @param target-db  the target db where the docs will be writting
: @param host  the hostname where the DHF is running
: @param port  the port on which the DHF is listening
: @param preformatted harmonisation options as xs:string
:
: @return  response xml
:
:)
declare function tf:run-harmonisation-flow-with-options(
    $entity as xs:string,
    $flow-name as xs:string,
    $source-db as xs:string,
    $target-db as xs:string,
    $host as xs:string,
    $port as xs:string,
    $dhf-options as xs:string
) as item()?
{
    let $throw-harmonization-error := function($message as xs:string) {
        fn:error(xs:QName("HARMONIZATION-ERROR"), $message)
    }
    let $base-url := "http://" || $host || ":" || $port
    let $flow as element(hub:flow)?  := tf:get-harmonisation-flow($base-url || $FLOW-URL, $entity, $flow-name, $source-db)
    ! (if ($HTTP-OK ne ./part1/http:response/http:code/fn:data()) then
            $throw-harmonization-error(" GET Flow http status was "||./part1/http:response/http:code/fn:string() )
        else ./part2/hub:flow)

    let $job-id := sem:uuid-string()
    let $options := $dhf-options  => xdmp:url-encode()
    let $_ := xdmp:log("XX running collector")
    let $uris := tf:run-collector($base-url || $RUN-COLLECTOR-URL, $job-id, $entity, $flow-name, $options, $source-db)
        ! (if ($HTTP-OK ne ./part1/http:response/http:code/fn:data()) then
            $throw-harmonization-error(" Run Collector http status was "||./part1/http:response/http:code/fn:string() )
           else ./part2 => fn:tokenize("\n")  )

    let $_ :=  xdmp:log("URIS = "||fn:count($uris) )

    let $_ := xdmp:log("XX running flow")
    return if ($uris) then tf:run-flow($base-url || $FLOW-URL, $uris, $job-id, $entity, $flow-name, $target-db, $options,  $flow, $source-db)
    ! (if ($HTTP-OK ne ./part1/http:response/http:code/fn:data()) then
        $throw-harmonization-error(" Run Flow http status was "||./part1/http:response/http:code/fn:string() )
       else ./part2)
    else xdmp:log("found no uris to process")
};

declare private function make-options(
    $entity as xs:string,
    $flow-name as xs:string
) as xs:string
{
    map:map() => map:with('entity', $entity)
              => map:with('flow', $flow-name)
              => map:with('flowType', $HARMONIZE-FLOW-TYPE)
              => map:with($cfg:INPUT-COLLECTIONS-KEY, $tcfg:UNIT-TEST-COLLECTION)
              => xdmp:to-json-string()
};

declare function run-harmonize-employees()
{
    let $host := $tcfg:TEST-HOST
    let $port := $tcfg:TEST-STAGING-PORT
    let $flow-name := "harmonizeEmployees"
    let $db := $cfg:STAGING-DB
    let $target-db := $cfg:FINAL-DB
    let $entity-name := "Employee"
    let $options := make-options($entity-name, $flow-name)
    return  tf:run-harmonisation-flow-with-options($entity-name, $flow-name, $db, $target-db, $host, $port, $options)
};


Framework for creating keyspaces as maps in place. Have a bug somewhere

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

   DITA Utilities

   Provides XQuery functions for working with DITA documents,
   including resolving keys and key references, hrefs,
   getting topic navigation titles, etc.

   Author: W. Eliot Kimber

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

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

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

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

(: Returns true if the specified element is of the specified DITA class.

   Handles the case of a failure to include the trailing space in the @class
   value (this was a bug in MarkLogic 3 in that it did not preserve trailing
   space in #CDATA attributes, corrected in MarkLogic 4).

   elem      - The element to test the class of.
   classSpec - A module/tagname pair, without spaces, e.g. "topic/p"

:)
declare function df:class($elem as element(), $classSpec as xs:string) as xs:boolean {
  (: Refinement from Bob Thomas :)
  let $result := matches($elem/@class, concat(' ', normalize-space($classSpec), ' |\$'))
  return $result
};

(: Gets the base class name of the context element, e.g. "topic/p" :)
 declare function df:getBaseClass($context as element()) as xs:string {
    (: @class value is always "- foo/bar fred/baz " or "+ foo/bar fred/baz " :)
    let $result as xs:string :=
      normalize-space(tokenize($context/@class, ' ')[2])
    return $result
};

declare function df:getHtmlClass($context as element()) as xs:string {
  let $result := if ($context/@outputclass)
                    then string($context/@outputclass)
                    else name($context)
  return $result
};

(: Gets the navigation title for a topic. If there is a navtitle title alternative,
   returns it, otherwise returns the topic's title.

 :)
declare function df:getNavtitleForTopic($topic as element()) as node()* {
   let $navtitle := if ($topic/*[df:class(., 'topic/titlealts')]/*[df:class(., 'topic/navtitle')])
                       then $topic/*[df:class(., 'topic/titlealts')]/*[df:class(., 'topic/navtitle')]/node()
                       else $topic/*[df:class(., 'topic/title')]/node()
   return $navtitle
};

(: Get all the DITA maps in the specified collection :)
declare function df:getMaps($collectionSpec as xs:string) as document-node()* {
  for $doc in collection($collectionSpec) where $doc[contains(/*/@class, ' map/map ')]
      return $doc
};

(: Get all the DITA topic documents in the specified collection, that is,
 : documents whose root element is of type topic/topic or <dita>
 :)
declare function df:getTopicDocs($collectionSpec as xs:string) as document-node()* {
  for $doc in collection($collectionSpec) where $doc[contains(/*/@class, ' topic/topic ') or /*/self::dita]
      return $doc
};

(: Get the title text for the specified element where the element is expected to have topic/title direct child
   Simply returns the text nodes of the title, filtering out elements that don't normally show up in output
   (<data>, <indexterm>, etc.)
:)
declare function df:getTitleText($elem as element()) as xs:string {
   let $title := df:getTitleElement($elem)
   (: FIXME: Implement the filtering, which needs to be a general function :)
   return string($title)
};

(: Get the title element for the specified element where the element is expected to have topic/title direct child :)
declare function df:getTitleElement($elem as element()) as element()? {
   ($elem/*[df:class(., 'topic/title')])[1]
};

(:~
 : Gets the effective value of the attribute, applying DITA-defined default
 : value rules when the attribute does not have a value.
 :
 :)
declare function df:getEffectiveAttributeValue($elem as element(), $attName as xs:string) as xs:string {
   let $att := $elem/@*[name(.) = $attName]
   return
     if ($att)
        then string($att)
        else (: Determined the implicit default, if any :)
         switch ($attName)
          case 'scope' return 'local'
          case 'format' return 'dita'
          default return ''

};

(:~
 : Resolve a topicref to its target topic or map element.
 :
 : Topicref must be a peer or local-scope topicref to a topic
 : or map.
 :
 : Returns the reference element or a <df:error> element if there
 : was some error.
 :
 : Returns empty sequence if the @scope is not
 : peer or local or @format is not dita or ditamap.
 :)
declare function df:resolveTopicRef($topicref as element()) as element()? {
   let $map as document-node() := root($topicref)
   let $format  as xs:string?  := df:getEffectiveAttributeValue($topicref, 'format')
   let $href  as xs:string?    := string($topicref/@href)
   let $keyref as xs:string?   := string($topicref/@keyref)
   let $scope as xs:string?    := df:getEffectiveAttributeValue($topicref, 'scope')

   return if (not(df:class($topicref, 'map/topicref')))
      then <df:error type="not-topicref" xmlns:df="http://dita-for-small-teams.org/xquery/modules/dita-utils">resolveTopicRef(): Element {name($topicref)} is not of class 'map/topicref', class is "{string($topicref/@class)}"</df:error>
      else (: It's a topicref, check the @format value:)
        if (not($format = ('dita', 'ditamap')) or
            ($scope != '' and (not($scope = ('local', 'peer')))))
           then <df:warn>Not format of 'dita' or 'ditamap' or not local or peer scope </df:warn>
           else
             let $targetUri as xs:string := df:getEffectiveTargetUri($topicref)
             let $targetFragId as xs:string :=
                 if (contains($topicref/@href, '#'))
                    then substring-after($topicref/@href, '#')
                    else ''
             return if ($targetUri = '' and $targetFragId = '')
                then ()
                else df:resolveTopicOrMapUri($topicref, $targetUri)

};

declare function df:resolveTopicOrMapUri($topicref as element(), $targetUri as xs:string)
   as element()? {
   let $targetResourcePart as xs:string :=
     if (contains($targetUri, '#'))
        then substring-before($targetUri, '#')
        else $targetUri
   let $topicFragId as xs:string :=
     if (contains($targetUri, '#'))
        then substring-after($targetUri, '#')
        else ''
   let $targetDoc :=
      if ($targetResourcePart != '')
         then
           let $baseUri := base-uri($topicref)
           let $resolvedUri :=
               if (not(starts-with($baseUri, '/')))
                  then (: Handle BaseX way of managing document URLs :)
                     let $parentUri := relpath:getParent($baseUri)
                     return relpath:newFile($parentUri, $targetResourcePart)
                  else resolve-uri($targetResourcePart, $baseUri)
           return doc($resolvedUri)
         else root($topicref)
   return
     if ($targetDoc/*[df:class(., 'topic/topic') or df:class(., 'topic/topic')] )
        then $targetDoc/*[1]
        else if ($targetDoc/*/*[df:class(., 'topic/topic')])
          then $targetDoc/*/*[df:class(., 'topic/topic')][1]
          else $targetDoc/*
 };

(: Give a topicref, return the effective URI of the ultimate target
 : of the topicref.
 :)
declare function df:getEffectiveTargetUri($refElem as element()) as xs:string {
  df:getEffectiveTargetUri(root($refElem)/*, $refElem)
};

(: Give a topicref, return the effective URI of the ultimate target
 : of the topicref.
 :)
declare function df:getEffectiveTargetUri($rootMap, $refElem as element()) as xs:string {
   let $effectiveUri :=
       if ($refElem/@keyref != '')
          then df:getEffectiveUriForKeyref($rootMap, $refElem)
          else string($refElem/@href)
   let $baseUri := relpath:getResourcePartOfUri($effectiveUri)
   let $fragId := relpath:getFragmentId($effectiveUri)
   let $resultBase :=
      if (string($refElem/@copy-to) != '' and not(df:inChunk($refElem)))
         then (: Copy-to in effect, not in a chunk :)
            let $copyTo as xs:string := $refElem/@copy-to
            let $fullUri as xs:string := string(resolve-uri($copyTo, base-uri($refElem)))
            return relpath:getRelativePath(relpath:getParent(base-uri($refElem)), $fullUri)
         else $baseUri
   return $resultBase

};

(:~
 : Given an element with a @keyref attribute, returns
 : the URI of the ultimate resource bound to the referenced
 : key, as defined in the specified root map (note, the map
 : needs to be a resolved map).
 :)
declare function df:getEffectiveUriForKeyref($rootMap, $refElem) as xs:string? {
   let $keyref as xs:string := string($refElem/@keyref)
   let $keyname as xs:string := if (contains($keyref, '/'))
        then tokenize($keyref, '/')[1]
        else $keyref
   (: At this point, need to look up the key reference in the
      the appropriate key space.

    :)
   return "key resolution not yet implemented"
};

(:
 : Returns true if the topicref element points to something and
 : has a @format value of 'ditamap'
 :
 :)
declare function df:isMapRef($topicref as element()) as xs:boolean {
  df:isTopicRef($topicref) and $topicref/@format = 'ditamap'
};

declare function df:isTopicRef($topicref as element()) as xs:boolean {
  df:class($topicref, 'map/topicref') and
      (($topicref/@href and $topicref/@href != '') or
       ($topicref/@keyref and $topicref/@keyref != ''))
};

(:~
 : Gets the map tree rooted at the input map. The result tree
 : always includes the specified map.
 :
 : Returns a single <mapTree> element containing tree items
 : for the complete map tree.
 :)
declare function df:getMapTree($map as document-node()) as element(mapTree)* {
  (: The map Uri is the object ID of the map tree, meaning every map tree
     is identified by the map from which it is constructed.

   :)
  <mapTree
     mapUri="{document-uri($map)}"
     timeStamp="{fn:current-dateTime()}"
  >
    { df:getMapTreeItem($map/*) }
  </mapTree>
};

(:~
 : Gets the map tree item for the input map. The result tree
 : always includes the specified map.
 :
 : Returns a single <treeItem> element representing the input map
 : and containing any subordinate maps.
 :)
declare function df:getMapTreeItem($mapElem as element()) as element(treeItem)* {
   let $label := if ($mapElem) then df:getTitleText($mapElem)
                           else "Failed to resolve reference to map "
   return <treeItem>
            <label>{$label}</label>
            <properties>
              <property name="maptype">{name($mapElem)}</property>
              <property name="uri">{bxutil:getPathForDoc(root($mapElem))}</property>
            </properties>
            <children>
              {df:getMapTreeItems($mapElem)}
            </children>
          </treeItem>
};

(:~
 : Get the tree of maps descending from a root map
 :
 : The result is returned a sequence of treeItem elements.
 :)
declare function df:getMapTreeItems($map as element()) as element(treeItem)* {
   let $maprefs := $map//*[df:isMapRef(.)]
   for $mapref in $maprefs
       let $mapElem := df:resolveTopicRef($mapref)
       return df:getMapTreeItem($mapElem)
};

(:~
 : Returns true if the context topicref is within
 : the context of a topicref that generates a new content
 : chunk (e.g., chunk="to-content select-branch").
 :)
 declare function df:inChunk($context) as xs:boolean {
   let $nearestChunkSpecifier as element()? := $context/ancestor::*[@chunk != ''][1]
   let $chunkSpec as xs:string := $nearestChunkSpecifier/@chunk
   let $result :=
       contains($chunkSpec, 'to-content') and
              (contains($chunkSpec, 'select-branch') or
               contains($chunkSpec, 'select-document'))
   return $result
};

declare function df:getMapDocForTreeItem($treeItem as element(treeItem)) as document-node()? {
   let $mapUri := string($treeItem/properties/property[@name = 'uri'])
   let $map := doc($mapUri)
   return $map
};

(:~
 : Constructs the key spaces root at the specified map document.
 :
 : Returns a single <keySpaceSet> element containing the key
 : spaces defined by the map.
 :)
declare function df:constructKeySpaces($map as document-node()) as element(keySpaceSet) {
  (: The map Uri is the object ID of the key space, meaning every key space
     is identified by the map from which it is constructed and the fully-qualified
     key scope.

   :)
   let $mapTree := df:getMapTree($map)
   let $keySpaces := df:constructKeySpacesForMapTree($mapTree)
   let $keySpaceSet := <keySpaceSet
       mapUri="{document-uri($map)}"
       timeStamp="{current-dateTime()}">
       {
         df:serializeKeySpacesMap($keySpaces)
       }
   </keySpaceSet>
   return $keySpaceSet
};

declare function df:serializeKeySpacesMap($keySpaces) as element(keySpace)* {
  (: The key spaces map is a map of scope names to key spaces.
     Each key space is a map of key names to key bindings.
     :)

  for $keyScopeName in map:keys($keySpaces)
      let $keySpace := map:get($keySpaces, $keyScopeName)
      return <keySpace scopeName="{$keyScopeName}">{
        for $keyName in map:keys($keySpace)
            return df:serializeKeyBindings($keyName, map:get($keySpace, $keyName))
            }</keySpace>
};

declare function df:serializeKeyBindings($keyName as xs:string, $keyBindings) as element()* {
  (:
     A key bindings is a sequence of key definitions,
     where each key definition is a map of property names to values:
     - key name: the key name
     - resource URI: The URI of the resource the key is bound to (if any)
     - key-definition element: the topicref element
         that is the data source for the key definition.
  :)
  <keyBindings keyName="{$keyName}">{
    for $keyBinding in $keyBindings
        return df:serializeKeyBinding($keyBinding)
  }</keyBindings>
};

declare function df:serializeKeyBinding($keyBinding) as element()* {
  <keyBinding><stuff/></keyBinding>
};

(:~
 : Constructs the key spaces for a map tree
 :
 : Returns a key space, one for the root (anonymous) key space,
 : one for each key scope defined in the map.
 :)
declare function df:constructKeySpacesForMapTree($mapTree as element(mapTree)) {
  (: Walk the map tree, using a breadth-first traveral per the DITA key space construction
     rules. Build up the key spaces from each map. There is a always a root key space anchored
     at the root map.

   :)
  let $rootMapItem := $mapTree/* (: The root map of the tree :)
  let $map := df:getMapDocForTreeItem($rootMapItem)
  let $keySpaceSet := df:constructKeySpacesForMap(
                     $map,
                     map { },
                     ('#root'))
  return $keySpaceSet
};

(:~
 : Constructs the key spaces for a single map
 :
 :
 :)
declare function df:constructKeySpacesForMap(
                    $map as document-node(),
                    $keySpaceSet,
                    $keyScopes as xs:string+) {

   (: Find the key-scope-defining elements and
      the key definitions and construct or add to the
      cooresponding key space maps.

      This might be easiest to do by simply walking
      the map element tree and accumulating scope
      context.
      :)

   let $keySpaces :=
       for $keyScope in $keyScopes
           return
           map { $keyScope :
                 map { $map :
                       map { 'key01' :
                              (map { 'keyName' : 'key01',
                                     'topicref' : <topicref keys="key01"/>,
                                     'resourceURI' : ''
                                   },
                               map { 'keyName' : 'key01',
                                     'topicref' : <topicref keys="key01 keyxxx"/>,
                                     'resourceURI' : 'foo/bar'
                                   }
                               ),
                               'key02' :
                               (map { 'keyName' : 'key02',
                                 'topicref' : <topicref keys="key02"/>,
                                 'resourceURI' : 'docs/topics/topic-01.dita'
                               })}}}

   return $keySpaces
};

(: ============== End of Module =================== :)

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

   DITA Utilities

   Provides XQuery functions for working with DITA documents,
   including resolving keys and key references, hrefs,
   getting topic navigation titles, etc.

   Author: W. Eliot Kimber

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

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

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

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

(: Returns true if the specified element is of the specified DITA class.

   Handles the case of a failure to include the trailing space in the @class
   value (this was a bug in MarkLogic 3 in that it did not preserve trailing
   space in #CDATA attributes, corrected in MarkLogic 4).

   elem      - The element to test the class of.
   classSpec - A module/tagname pair, without spaces, e.g. "topic/p"

:)
declare function df:class($elem as element(), $classSpec as xs:string) as xs:boolean {
  (: Refinement from Bob Thomas :)
  let $result := matches($elem/@class, concat(' ', normalize-space($classSpec), ' |\$'))
  return $result
};

(: Gets the base class name of the context element, e.g. "topic/p" :)
 declare function df:getBaseClass($context as element()) as xs:string {
    (: @class value is always "- foo/bar fred/baz " or "+ foo/bar fred/baz " :)
    let $result as xs:string :=
      normalize-space(tokenize($context/@class, ' ')[2])
    return $result
};

declare function df:getHtmlClass($context as element()) as xs:string {
  let $result := if ($context/@outputclass)
                    then string($context/@outputclass)
                    else name($context)
  return $result
};

(: Gets the navigation title for a topic. If there is a navtitle title alternative,
   returns it, otherwise returns the topic's title.

 :)
declare function df:getNavtitleForTopic($topic as element()) as node()* {
   let $navtitle := if ($topic/*[df:class(., 'topic/titlealts')]/*[df:class(., 'topic/navtitle')])
                       then $topic/*[df:class(., 'topic/titlealts')]/*[df:class(., 'topic/navtitle')]/node()
                       else $topic/*[df:class(., 'topic/title')]/node()
   return $navtitle
};

(: Get all the DITA maps in the specified collection :)
declare function df:getMaps($collectionSpec as xs:string) as document-node()* {
  for $doc in collection($collectionSpec) where $doc[contains(/*/@class, ' map/map ')]
      return $doc
};

(: Get all the DITA topic documents in the specified collection, that is,
 : documents whose root element is of type topic/topic or <dita>
 :)
declare function df:getTopicDocs($collectionSpec as xs:string) as document-node()* {
  for $doc in collection($collectionSpec) where $doc[contains(/*/@class, ' topic/topic ') or /*/self::dita]
      return $doc
};

(: Get the title text for the specified element where the element is expected to have topic/title direct child
   Simply returns the text nodes of the title, filtering out elements that don't normally show up in output
   (<data>, <indexterm>, etc.)
:)
declare function df:getTitleText($elem as element()) as xs:string {
   let $title := df:getTitleElement($elem)
   (: FIXME: Implement the filtering, which needs to be a general function :)
   return string($title)
};

(: Get the title element for the specified element where the element is expected to have topic/title direct child :)
declare function df:getTitleElement($elem as element()) as element()? {
   ($elem/*[df:class(., 'topic/title')])[1]
};

(:~
 : Gets the effective value of the attribute, applying DITA-defined default
 : value rules when the attribute does not have a value.
 :
 :)
declare function df:getEffectiveAttributeValue($elem as element(), $attName as xs:string) as xs:string {
   let $att := $elem/@*[name(.) = $attName]
   return
     if ($att)
        then string($att)
        else (: Determined the implicit default, if any :)
         switch ($attName)
          case 'scope' return 'local'
          case 'format' return 'dita'
          default return ''

};

(:~
 : Resolve a topicref to its target topic or map element.
 :
 : Topicref must be a peer or local-scope topicref to a topic
 : or map.
 :
 : Returns the reference element or a <df:error> element if there
 : was some error.
 :
 : Returns empty sequence if the @scope is not
 : peer or local or @format is not dita or ditamap.
 :)
declare function df:resolveTopicRef($topicref as element()) as element()? {
   let $map as document-node() := root($topicref)
   let $format  as xs:string?  := df:getEffectiveAttributeValue($topicref, 'format')
   let $href  as xs:string?    := string($topicref/@href)
   let $keyref as xs:string?   := string($topicref/@keyref)
   let $scope as xs:string?    := df:getEffectiveAttributeValue($topicref, 'scope')

   return if (not(df:class($topicref, 'map/topicref')))
      then <df:error type="not-topicref" xmlns:df="http://dita-for-small-teams.org/xquery/modules/dita-utils">resolveTopicRef(): Element {name($topicref)} is not of class 'map/topicref', class is "{string($topicref/@class)}"</df:error>
      else (: It's a topicref, check the @format value:)
        if (not($format = ('dita', 'ditamap')) or
            ($scope != '' and (not($scope = ('local', 'peer')))))
           then <df:warn>Not format of 'dita' or 'ditamap' or not local or peer scope </df:warn>
           else
             let $targetUri as xs:string := df:getEffectiveTargetUri($topicref)
             let $targetFragId as xs:string :=
                 if (contains($topicref/@href, '#'))
                    then substring-after($topicref/@href, '#')
                    else ''
             return if ($targetUri = '' and $targetFragId = '')
                then ()
                else df:resolveTopicOrMapUri($topicref, $targetUri)

};

declare function df:resolveTopicOrMapUri($topicref as element(), $targetUri as xs:string)
   as element()? {
   let $targetResourcePart as xs:string :=
     if (contains($targetUri, '#'))
        then substring-before($targetUri, '#')
        else $targetUri
   let $topicFragId as xs:string :=
     if (contains($targetUri, '#'))
        then substring-after($targetUri, '#')
        else ''
   let $targetDoc :=
      if ($targetResourcePart != '')
         then
           let $baseUri := base-uri($topicref)
           let $resolvedUri :=
               if (not(starts-with($baseUri, '/')))
                  then (: Handle BaseX way of managing document URLs :)
                     let $parentUri := relpath:getParent($baseUri)
                     return relpath:newFile($parentUri, $targetResourcePart)
                  else resolve-uri($targetResourcePart, $baseUri)
           return doc($resolvedUri)
         else root($topicref)
   return
     if ($targetDoc/*[df:class(., 'topic/topic') or df:class(., 'topic/topic')] )
        then $targetDoc/*[1]
        else if ($targetDoc/*/*[df:class(., 'topic/topic')])
          then $targetDoc/*/*[df:class(., 'topic/topic')][1]
          else $targetDoc/*
 };

(: Give a topicref, return the effective URI of the ultimate target
 : of the topicref.
 :)
declare function df:getEffectiveTargetUri($refElem as element()) as xs:string {
  df:getEffectiveTargetUri(root($refElem)/*, $refElem)
};

(: Give a topicref, return the effective URI of the ultimate target
 : of the topicref.
 :)
declare function df:getEffectiveTargetUri($rootMap, $refElem as element()) as xs:string {
   let $effectiveUri :=
       if ($refElem/@keyref != '')
          then df:getEffectiveUriForKeyref($rootMap, $refElem)
          else string($refElem/@href)
   let $baseUri := relpath:getResourcePartOfUri($effectiveUri)
   let $fragId := relpath:getFragmentId($effectiveUri)
   let $resultBase :=
      if (string($refElem/@copy-to) != '' and not(df:inChunk($refElem)))
         then (: Copy-to in effect, not in a chunk :)
            let $copyTo as xs:string := $refElem/@copy-to
            let $fullUri as xs:string := string(resolve-uri($copyTo, base-uri($refElem)))
            return relpath:getRelativePath(relpath:getParent(base-uri($refElem)), $fullUri)
         else $baseUri
   return $resultBase

};

(:~
 : Given an element with a @keyref attribute, returns
 : the URI of the ultimate resource bound to the referenced
 : key, as defined in the specified root map (note, the map
 : needs to be a resolved map).
 :)
declare function df:getEffectiveUriForKeyref($rootMap, $refElem) as xs:string? {
   let $keyref as xs:string := string($refElem/@keyref)
   let $keyname as xs:string := if (contains($keyref, '/'))
        then tokenize($keyref, '/')[1]
        else $keyref
   (: At this point, need to look up the key reference in the
      the appropriate key space.

    :)
   return "key resolution not yet implemented"
};

(:
 : Returns true if the topicref element points to something and
 : has a @format value of 'ditamap'
 :
 :)
declare function df:isMapRef($topicref as element()) as xs:boolean {
  df:isTopicRef($topicref) and $topicref/@format = 'ditamap'
};

declare function df:isTopicRef($topicref as element()) as xs:boolean {
  df:class($topicref, 'map/topicref') and
      (($topicref/@href and $topicref/@href != '') or
       ($topicref/@keyref and $topicref/@keyref != ''))
};

(:~
 : Gets the map tree rooted at the input map. The result tree
 : always includes the specified map.
 :
 : Returns a single <mapTree> element containing tree items
 : for the complete map tree.
 :)
declare function df:getMapTree($map as document-node()) as element(mapTree)* {
  (: The map Uri is the object ID of the map tree, meaning every map tree
     is identified by the map from which it is constructed.

   :)
  <mapTree
     mapUri="{document-uri($map)}"
     timeStamp="{fn:current-dateTime()}"
  >
    { df:getMapTreeItem($map/*) }
  </mapTree>
};

(:~
 : Gets the map tree item for the input map. The result tree
 : always includes the specified map.
 :
 : Returns a single <treeItem> element representing the input map
 : and containing any subordinate maps.
 :)
declare function df:getMapTreeItem($mapElem as element()) as element(treeItem)* {
   let $label := if ($mapElem) then df:getTitleText($mapElem)
                           else "Failed to resolve reference to map "
   return <treeItem>
            <label>{$label}</label>
            <properties>
              <property name="maptype">{name($mapElem)}</property>
              <property name="uri">{bxutil:getPathForDoc(root($mapElem))}</property>
            </properties>
            <children>
              {df:getMapTreeItems($mapElem)}
            </children>
          </treeItem>
};

(:~
 : Get the tree of maps descending from a root map
 :
 : The result is returned a sequence of treeItem elements.
 :)
declare function df:getMapTreeItems($map as element()) as element(treeItem)* {
   let $maprefs := $map//*[df:isMapRef(.)]
   for $mapref in $maprefs
       let $mapElem := df:resolveTopicRef($mapref)
       return df:getMapTreeItem($mapElem)
};

(:~
 : Returns true if the context topicref is within
 : the context of a topicref that generates a new content
 : chunk (e.g., chunk="to-content select-branch").
 :)
 declare function df:inChunk($context) as xs:boolean {
   let $nearestChunkSpecifier as element()? := $context/ancestor::*[@chunk != ''][1]
   let $chunkSpec as xs:string := $nearestChunkSpecifier/@chunk
   let $result :=
       contains($chunkSpec, 'to-content') and
              (contains($chunkSpec, 'select-branch') or
               contains($chunkSpec, 'select-document'))
   return $result
};

declare function df:getMapDocForTreeItem($treeItem as element(treeItem)) as document-node()? {
   let $mapUri := string($treeItem/properties/property[@name = 'uri'])
   let $map := doc($mapUri)
   return $map
};

(:~
 : Constructs the key spaces root at the specified map document.
 :
 : Returns a single <keySpaceSet> element containing the key
 : spaces defined by the map.
 :)
declare function df:constructKeySpaces($map as document-node()) as element(keySpaceSet) {
  (: The map Uri is the object ID of the key space, meaning every key space
     is identified by the map from which it is constructed and the fully-qualified
     key scope.

   :)
   let $mapTree := df:getMapTree($map)
   let $keySpaces := df:constructKeySpacesForMapTree($mapTree)
   let $keySpaceSet := <keySpaceSet
       mapUri="{document-uri($map)}"
       timeStamp="{current-dateTime()}">
       {
         df:serializeKeySpacesMap($keySpaces)
       }
   </keySpaceSet>
   return $keySpaceSet
};

declare function df:serializeKeySpacesMap($keySpaces) as element(keySpace)* {
  (: The key spaces map is a map of scope names to key spaces.
     Each key space is a map of key names to key bindings.
     :)

  for $keyScopeName in map:keys($keySpaces)
      let $keySpace := map:get($keySpaces, $keyScopeName)
      return <keySpace scopeName="{$keyScopeName}">{
        for $keyName in map:keys($keySpace)
            return df:serializeKeyBindings($keyName, map:get($keySpace, $keyName))
            }</keySpace>
};

declare function df:serializeKeyBindings($keyName as xs:string, $keyBindings) as element()* {
  (:
     A key bindings is a sequence of key definitions,
     where each key definition is a map of property names to values:
     - key name: the key name
     - resource URI: The URI of the resource the key is bound to (if any)
     - key-definition element: the topicref element (without nested topicrefs)
         that is the data source for the key definition.
     - topicref treelocation: A tree location, against the element tree, that locates
         the topicref within its containing map (NOTE: might be possible to replace
         this with some sort of element lookup key ala generate-id()).
     - containing map: The URI of the map that contains the key-defining element
  :)
  <keyBindings keyName="{$keyName}">{
    for $keyBinding in $keyBindings
        return df:serializeKeyBinding($keyBinding)
  }</keyBindings>
};

declare function df:serializeKeyBinding($keyBinding) as element()* {
  <keyBinding><stuff/></keyBinding>
};

(:~
 : Constructs the key spaces for a map tree
 :
 : Returns a key space, one for the root (anonymous) key space,
 : one for each key scope defined in the map.
 :)
declare function df:constructKeySpacesForMapTree($mapTree as element(mapTree)) {
  (: Walk the map tree, using a breadth-first traveral per the DITA key space construction
     rules. Build up the key spaces from each map. There is a always a root key space anchored
     at the root map.

   :)
  let $rootMapItem := $mapTree/* (: The root map of the tree :)
  let $map := df:getMapDocForTreeItem($rootMapItem)
  let $keySpaces := df:constructKeySpacesForMap(
                     $map,
                     map { '#root' : map { } },
                     ('#root'))
  return $keySpaces
};

(:~
 : Constructs the key spaces for a single map
 :
 :
 :)
declare function df:constructKeySpacesForMap(
                    $map as document-node(),
                    $keySpaces,
                    $keyScopes as xs:string+) {
   let $keySpace := map { 'key01' : map { 'keydef' : <topicref/>, 'resourceURI' : 'foo/bar' } }
   let $result := for $keyScope in $keyScopes
                      return map:put($keySpaces,
                                     $keyScope,
                                     $keySpace)
   return $result
};

(: ============== End of Module =================== :)


edit and remove user feature added

edit and remove user feature added

xquery version "1.0";

(: ~
 : Module Name:             edit context webpage and XQuery
 :
 : Module Version           2.0
 :
 : Date                     31st July 2009
 :
 : Copyright                The cagrid consortium
 :
 : Module overview          edit context information
 :
 :)

(:~
 :    @author Rakesh Dhaval
      @author Puneet Mathur
 :    @version 0.2
 :
 :    User Management Page
~ :)

  import module namespace
  lib-forms="http://www.cagrid.org/xquery/library/forms"
  at "../edit/m-lib-forms.xquery";

  import module namespace
  lib-util="http://www.cagrid.org/xquery/library/util"
  at "../library/m-lib-util.xquery";

  import module namespace
  lib-rendering="http://www.cagrid.org/xquery/library/rendering"
  at "../web/m-lib-rendering.xquery";

  import module namespace
  lib-make-admin-item="http://www.cagrid.org/xquery/library/make-admin-item"
  at "../edit/m-lib-make-admin-item.xquery";

declare namespace openMDR = "http://www.cagrid.org/schema/openMDR";
declare namespace ISO11179= "http://www.cagrid.org/schema/ISO11179";
declare namespace session="http://exist-db.org/xquery/session";
declare namespace request="http://exist-db.org/xquery/request";
declare namespace response="http://exist-db.org/xquery/response";
declare namespace exist = "http://exist.sourceforge.net/NS/exist";
declare namespace util="http://exist-db.org/xquery/util";
declare namespace xmldb="http://exist-db.org/xquery/xmldb";

declare function local:success-page()
{
   let $calling-page := request:get-parameter("calling-page","")
   return

        if(local:validateUser()=true()) then
        (
        	local:process()
        )
        else(

            Access Denied !! Only users of "dba" group have access

            Return to maintenance menu

        )

};

declare function local:new-user() as element()*
{

    let $name := request:get-parameter("name", ()),
    $grp := request:get-parameter("groups", ()),
    $groups := if ($grp) then
        (
            if(contains($grp, ",")) then
            (
                tokenize($grp, "\s*,\s*")
            )
            else
            (
                $grp
            )
        )else(),
    $pass1 := request:get-parameter("pass1", ""),
    $pass2 := request:get-parameter("pass2", "")
    return

    if($name eq "") then
    (

	Error : UserName is missing !!!!

 {local:correct-user(-1, $name, $grp)}

    )
   (:
   else if($name != "") then
   (
                for $user in doc("/db/system/users.xml")//users/user
                    let $stored_username := xs:string($user/@name)
 	 return(
 	 	if($name eq $stored_username) then
 	 	(

			Error : UserName Already Exists !! Chooose a different Username!!!!

 {local:correct-user(-1, $name, $grp)}

		)
		else()
	   )

   ) :)

    else if($grp eq "") then
    (

	Error : GroupName is missing !! Please specify atleast one group!!!!!

 {local:correct-user(-1, $name, $grp)}

    )

    else if($pass1 != $pass2 or $pass1 eq "") then
        (

	Error : Either Password is missing or Passwords are not identical !!!!

 {local:correct-user(-1, $name, $grp)}

        )
        else(

            xmldb:create-user($name, $pass1, $groups, ""),

            User "{$name}" Created !!!

{local:display()}

          )
};

declare function local:display()
{

      <form  method="get" action="{session:encode-url(request:get-uri())}">

                Name
                Groups

            {
                for $user in doc("/db/system/users.xml")//users/user
                    let $name := xs:string($user/@name),
                    $groups := string-join($user/group, ", ")
                order by $name return

                        <input type="radio" name="uid" value="{$user/@uid}"/>
                        {$name}
                        {$groups}

            }

                    <input type="submit" name="action" value="Edit"/>
                    <input type="submit" name="action" value="New User"/>
                    <input type="submit" name="action" value="Remove"/>

        {
                let $action := request:get-parameter("action", ""),
                $uid := request:get-parameter("uid", ()) return
                if($action eq "Edit") then
                    (
                            if(empty($uid) )then
 	             (

		            <be/>
Error : Select a User to Update !!!!

	            )
      	          else
	            (
                         		let $user := doc("/db/system/users.xml")//users/user[@uid = $uid] return
        			if( ($user/@name) eq "admin" and xmldb:get-current-user() != "admin" ) then
	      	              (

 Error : You do not have permission to Edit the Admin User !!!!

	       	             )
       	             	          else(
                         		local:edit-user(xs:integer($uid), $user/@name, string-join($user/group, ", "))
                         	          )
                           )
                    )
                    else if($action eq "New User") then
                    (
                        local:edit-user(-1, "", "")
                    )else()

        }

Return to maintenance menu
     </form>

} ;

declare function local:correct-user($uid as xs:integer, $name as xs:string, $groups as xs:string)  as element()
{

    <form action="{session:encode-url(request:get-uri())}" method="get">
        {local:edit-user($uid, $name, $groups)}
        <input type="hidden" name="panel" value="users"/>
    </form>

};

declare function local:edit-user($uid as xs:integer, $name as xs:string, $groups as xs:string) as element()
{

            User:
            {
                if($name eq "") then
                (
                    <input type="text" name="name"/>
                )
                else
                (
                    <input type="hidden" name="name" value="{$name}"/>{$name}
                )
            }

            Groups:
            <input type="text" name="groups" value="{$groups}"/>
            A comma-separated list of groups.
            Note: non-existing groups will be created automatically.

            Password:
            <input type="password" name="pass1"/>

            Repeat:
            <input type="password" name="pass2"/>

         {
            if($uid ge 0) then
            (
                <input type="checkbox" name="nopass"/> Leave password unchanged.
            )else()
        }

        {
            if($uid lt 0) then
            (

                <input type="submit" name="action" value="Create"/>
	<input type="submit" name="action" value="Cancel"/>
            )
            else
            (

                <input type="submit" name="action" value="Change"/>
	 <input type="submit" name="action" value="Cancel"/>
            )

        }

        <input type="hidden" name="uid" value="{$uid}"/>

 };

declare function local:process()  as element()*
{
    let $action := request:get-parameter("action", "") ,
    $session_user  := session:get-attribute("username"),
    $current_user := xmldb:get-current-user()
    return
        if($action eq "Create") then
        (
            local:new-user()
        )
       else if($action eq "Change") then
        (
            local:update-user()
        )

         else if($action eq "Remove") then
        (
            local:remove-user()
        )
        else if($action eq "Cancel") then
        (

	       {local:display()}

        )
        else(

	       {local:display()}

        )

};

declare function local:validateUser() as xs:boolean
{
   let $hostname :=  request:get-hostname()

   let $resource_location := concat("xmldb:exist://", $hostname, ":8080/exist/xmlrpc/db")
   let $user := session:get-attribute("username")
   let $password := session:get-attribute("password")
   let $is-ssl :=  lib-util:checkSSL()
   return
           if(xmldb:is-admin-user($user)) then
           (
            	if (xmldb:login($resource_location, $user, $password))
              then (
		true()
	)
	else(
		false()
	)
          )
          else(
	          false()
          )

};

declare function local:update-user() as element()*
{

    let $name := request:get-parameter("name", ()),
    $grp := request:get-parameter("groups", ()),
    $groups := if ($grp) then
        (
            if(contains($grp, ",")) then
            (
                tokenize($grp, "\s*,\s*")
            )
            else
            (
                $grp
            )
        )else(),
    $pass1 := request:get-parameter("pass1", ""),
    $pass2 := request:get-parameter("pass2", ""),
    $nopass := request:get-parameter("nopass", ()),
    $pass := if($nopass) then () else $pass1,
    $uid := request:get-parameter("uid", "") return

        if(not($nopass) and $pass1 ne $pass2)then
        (

Passwords are not identical.
,
            local:correct-user($uid, $name, $grp)
        )
        else if(empty($groups)) then
        (

Please specify one group at least.
,
            local:correct-user($uid, $name, $grp)
        )
        else
        (
            xmldb:change-user($name, $pass, $groups, ""),

            if(xmldb:get-current-user() eq $name) then
            (
                session:set-attribute("password", $pass)
            )else(),

            User "{$name}" Updated !!!

{local:display()}

        )

};

declare function local:remove-user()
{
    if(empty(request:get-parameter("uid", ())) )then
    (

            Error : Select a User to Remove !!!!

Return to User Management

    )
   else
   (
    let $uid := request:get-parameter("uid", ()),
    $name := doc("/db/system/users.xml")//user[@uid = $uid]/@name cast as xs:string return

        if($name eq xmldb:get-current-user() or $name eq "admin") then
        (

            You cannot remove the Admin User or Current User!!!!

Return to User Management

        )
        else if (xmldb:is-admin-user($name) and xmldb:get-current-user() != "admin") then
        (

            You do not have the permission to remove the user of "dba" group!!!!

Return to User Management

      )
      else
      (

            User "{$name}" Removed !!!

{local:display()}

        )
     )
};

declare option exist:serialize "media-type=text/html method=xhtml doctype-public=-//W3C//DTD XHTML 1.0 Transitional//EN doctype-system=http://www.w3.org/TR/2002/REC-xhtml1-20020801/DTD/xhtml1-transitional.dtd";

   session:create(),
   let $id := request:get-parameter('id','')
   let $title as xs:string := concat(" User Management", $id)

   return
      lib-rendering:txfrm-webpage(
      $title,
            local:success-page()
         )

xquery version "1.0";

(: ~
 : Module Name:             edit context webpage and XQuery
 :
 : Module Version           2.0
 :
 : Date                     31st July 2009
 :
 : Copyright                The cagrid consortium
 :
 : Module overview          edit context information
 :
 :)

(:~
 :    @author Rakesh Dhaval
      @author Puneet Mathur
 :    @version 0.2
 :
 :    User Management Page
~ :)

  import module namespace
  lib-forms="http://www.cagrid.org/xquery/library/forms"
  at "../edit/m-lib-forms.xquery";

  import module namespace
  lib-util="http://www.cagrid.org/xquery/library/util"
  at "../library/m-lib-util.xquery";

  import module namespace
  lib-rendering="http://www.cagrid.org/xquery/library/rendering"
  at "../web/m-lib-rendering.xquery";

  import module namespace
  lib-make-admin-item="http://www.cagrid.org/xquery/library/make-admin-item"
  at "../edit/m-lib-make-admin-item.xquery";

declare namespace openMDR = "http://www.cagrid.org/schema/openMDR";
declare namespace ISO11179= "http://www.cagrid.org/schema/ISO11179";
declare namespace session="http://exist-db.org/xquery/session";
declare namespace request="http://exist-db.org/xquery/request";
declare namespace response="http://exist-db.org/xquery/response";
declare namespace exist = "http://exist.sourceforge.net/NS/exist";
declare namespace util="http://exist-db.org/xquery/util";
declare namespace xmldb="http://exist-db.org/xquery/xmldb";

declare function local:success-page()
{
   let $calling-page := request:get-parameter("calling-page","")
   return

      	(:

              		   { local: validateUser() }

              		   { local:process() }

        :)

        if(local: validateUser()=true()) then
        (
        	local:process()
        )
        else(

            Access Denied !! Only users of "dba" group have access

            Return to maintenance menu

        )

};

declare function local:new-user() as element()*
{
    let $name := request:get-parameter("name", ()),
    $grp := request:get-parameter("groups", ()),
    $groups := $grp,

    $pass1 := request:get-parameter("pass1", ""),
    $pass2 := request:get-parameter("pass2", "")
    return

    if($pass1 != $pass2) then
        (
               local:correct-user(-1, $name, $grp)
        )
        else(
            (:  xmldb:create-user($name, $pass1, $grp, $home)
            xmldb:create-user("cde", "newtest", "qwert", "")
	local:testform(),
	local:display():)

              		   { xmldb:create-user($name, $pass1, $grp, "") }

              		  { local:display() }

          )
           (:
        local:testform(),

             Account Created

            Return to User Management

	:)

};

declare function local:display()
{

      <form  method="get" action="{session:encode-url(request:get-uri())}">

                Name
                Groups

            {
                for $user in doc("/db/system/users.xml")//users/user
                    let $name := xs:string($user/@name),
                    $groups := string-join($user/group, ", ")
                order by $name return

                        <input type="radio" name="uid" value="{$user/@uid}"/>
                        {$name}
                        {$groups}

            }

                    <input type="submit" name="action" value="Edit"/>
                    <input type="submit" name="action" value="New User"/>
                    <input type="submit" name="action" value="Remove"/>

        {
                let $action := request:get-parameter("action", ""),
                $uid := request:get-parameter("uid", "") return
	 if($action eq "New User") then
                (
                       local:edit-user(-1, "", "")

                 )else
                 (
                  	(:        local:testform()  :)
                 )
        }

Return to maintenance menu
     </form>

} ;

declare function local:correct-user($uid as xs:integer, $name as xs:string, $groups as xs:string)  as element()
{

    <form action="{session:encode-url(request:get-uri())}" method="get">
        {local:edit-user($uid, $name, $groups)}
        <input type="hidden" name="panel" value="users"/>
    </form>

};

declare function local:edit-user($uid as xs:integer, $name as xs:string, $groups as xs:string) as element()
{

            User:
            {
                if($name eq "") then
                (
                    <input type="text" name="name"/>
                )
                else
                (
                    <input type="hidden" name="name" value="{$name}"/>{$name}
                )
            }

            Groups:
            <input type="text" name="groups" value="{$groups}"/>
            A comma-separated list of groups.
            Note: non-existing groups will be created automatically.

            Password:
            <input type="password" name="pass1"/>

            Repeat:
            <input type="password" name="pass2"/>

        {
            if($uid lt 0) then
            (
                <input type="submit" name="action" value="Create"/>
            )
            else()
        }

        <input type="hidden" name="uid" value="{$uid}"/>

 };

declare function local:process()  as element()*
{
    let $action := request:get-parameter("action", "") ,
    $session_user  := session:get-attribute("username"),
    $current_user := xmldb:get-current-user()
    return
        if($action eq "Create") then
        (
            local:new-user()
        )
        (:
        else
        (
        	request:set-current-user($session_user,$current_user) ,

      	local:display()

              		   Current User : { xmldb:get-current-user() }

              		   Session User : {$session_user }

              		  {xmldb:get-permissions('/db/system')}

              		  {xmldb:permissions-to-string(511)}

        )
        else
        (

              		   Current User : { xmldb:get-current-user() }

              		  {xmldb:get-permissions('/db/system')}

              		  {xmldb:permissions-to-string(504)}

              		  { xmldb:set-collection-permissions('/db/system', 'rty','"dba', 511) }

        )
:)

        else(

              		    { local:display() }

        )

};

declare function local:testform()
{

           <form name="login" method="post" class="cagridForm" action="{session:encode-url(request:get-uri())}">

                       Please Login

                       Username:
                       <input name="user" type="text" size="20"/>

                       Password:
                       <input name="pass" type="password" size="20"/>

                       <input type="submit" value="login"/>

           </form>

};

declare function local:validateUser() as xs:boolean
{
   let $hostname :=  request:get-hostname()

   let $resource_location := concat("xmldb:exist://", $hostname, ":8080/exist/xmlrpc/db")
   let $user := session:get-attribute("username")
   let $password := session:get-attribute("password")
   let $is-ssl :=  lib-util:checkSSL()
   return
           if(xmldb:is-admin-user($user)) then
           (
            	if (xmldb:login($resource_location, $user, $password))
              then (
		true()
	)
	else(
		false()
	)
          )
          else(
	          false()
          )

};

declare option exist:serialize "media-type=text/html method=xhtml doctype-public=-//W3C//DTD XHTML 1.0 Transitional//EN doctype-system=http://www.w3.org/TR/2002/REC-xhtml1-20020801/DTD/xhtml1-transitional.dtd";

   session:create(),
   let $id := request:get-parameter('id','')
   let $title as xs:string := concat(" User Management", $id)

   return
      lib-rendering:txfrm-webpage(
      $title,
            local:success-page()
         )