Minor fixes around group collection path truncation

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

xquery version "1.0";

(:~
    The core XQuery script for the bibliographic demo. It receives a template XML document
    from the controller and expands it. If a search was trigger by the user, the script
    proceeds as follows:

the input form parameters are transformed into a simple XML structure
            to describe the query

an XPath string is generated from the XML query structure

the XPath is executed and the sort criteria applied

query results, XML query and sort criteria are stored into the HTTP session

the template is expanded, forms are regenerated to match the query

    To apply a filter to an existing query, we just extend the XML representation
    of the query.
:)
declare namespace biblio="http:/exist-db.org/xquery/biblio";
declare namespace group="http://exist-db.org/mods/sharing/group";
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 xmldb="http://exist-db.org/xquery/xmldb";
declare namespace functx = "http://www.functx.com";

import module namespace config="http://exist-db.org/mods/config" at "config.xqm";
import module namespace jquery="http://exist-db.org/xquery/jquery" at "resource:org/exist/xquery/lib/jquery.xql";

import module namespace mods="http://www.loc.gov/mods/v3" at "retrieve-mods.xql";
import module namespace sort="http://exist-db.org/xquery/sort" at "java:org.exist.xquery.modules.sort.SortModule";
import module namespace security="http://exist-db.org/mods/security" at "security.xqm";
import module namespace sharing="http://exist-db.org/mods/sharing" at "sharing.xqm";

declare option exist:serialize "method=xhtml media-type=application/xhtml+xml omit-xml-declaration=no enforce-xhtml=yes";

declare function functx:replace-first( $arg as xs:string?, $pattern as xs:string, $replacement as xs:string )  as xs:string {
   replace($arg, concat('(^.*?)', $pattern),
             concat('$1',$replacement))
 } ;

(:~
    Simple mapping from field names to an XPath expression
:)
declare variable $biblio:FIELDS :=
	<fields>
		<field name="Title">(
		mods:mods[ft:query(mods:titleInfo, '$q', $options)]
		      union
		mods:mods[ft:query(mods:relatedItem/mods:titleInfo, '$q', $options)]
		)</field>
		<field name="Name">(
		mods:mods[ft:query(mods:name, '$q', $options)]
		      union
		mods:mods[ft:query(mods:relatedItem/mods:name, '$q', $options)]
		)</field>
		<field name="Date">(
		mods:mods[ft:query(mods:originInfo/mods:dateCreated, '$q', $options)]
		      union
		mods:mods[ft:query(mods:originInfo/mods:dateIssued, '$q', $options)]
		      union
		mods:mods[ft:query(mods:originInfo/mods:dateCaptured, '$q', $options)]
		      union
		mods:mods[ft:query(mods:originInfo/mods:copyrightDate, '$q', $options)]
		      union
		mods:mods[ft:query(mods:relatedItem/mods:originInfo/mods:dateCreated, '$q', $options)]
		      union
		mods:mods[ft:query(mods:relatedItem/mods:originInfo/mods:dateIssued, '$q', $options)]
		      union
		mods:mods[ft:query(mods:relatedItem/mods:originInfo/mods:dateCaptured, '$q', $options)]
		      union
		mods:mods[ft:query(mods:relatedItem/mods:originInfo/mods:copyrightDate, '$q', $options)]
		)</field>
		<field name="Abstract">mods:mods[ft:query(mods:abstract, '$q', $options)]</field>
        <field name="Note">mods:mods[ft:query(mods:note, '$q', $options)]</field>
        <field name="Subject">mods:mods[ft:query(mods:subject, '$q', $options)]</field>
        <field name="All">(
        mods:mods[ft:query(.//*, '$q', $options)]
		)</field>

	</fields>;

(:
    Default template to be used for form generation if no
    query was specified
:)
declare variable $biblio:TEMPLATE_QUERY :=
    <query>
        <collection>{$config:mods-root}</collection>
        <and>
            <field m="1" name="All"></field>
        </and>
    </query>;

(:~
    Regenerate the HTML form to match the query, e.g. after adding more filter
    clauses.
:)
declare function biblio:form-from-query($query as element()) as element()+ {
    for $field at $pos in $query//field
    return

            {
                let $operator :=
                    if ($field/preceding-sibling::*) then
                        $field/../local-name(.)
                    else
                        ()
                return
                    <select name="operator{$pos}">
                    { if (empty($operator)) then attribute style { "display: none;" } else () }
                    {
                        for $opt in ("and", "or", "not")
                        return
                            <option>
                            {
                                if ($opt eq $operator) then
                                    attribute selected { "selected" }
                                else
                                    ()
                            }
                            { $opt }
                            </option>
                    }
                    </select>
            }

                <jquery:input name="input{$pos}" value="{$field/string()}">
                    <jquery:autocomplete url="autocomplete.xql"
                        width="300" multiple="false"
                        matchContains="false"
                        paramsCallback="autocompleteCallback">
                    </jquery:autocomplete>
                </jquery:input>

                in <select name="field{$pos}">
                {
                    for $f in $biblio:FIELDS/field
                    return
                        <option>
                            { if ($f/@name eq $field/@name) then attribute selected { "selected" } else () }
                            {$f/@name/string()}
                        </option>
                }
                </select>

};

declare function local:create-group-collections-string($group-ids as xs:string+,  $group-collections as xs:string*) as xs:string*
{
    if(not(empty($group-collections)))then(
        fn:concat(
            "collection(",
            fn:string-join(
                for $shared-collection in $group-collections return
                    fn:concat("'", $shared-collection, "'")
            , ","),
            ")//"
        )
    )
    else
    (
        util:log("debug", fn:concat("biblio: could not find any shared collections for the group with id: ", $group-ids)),
        fn:concat("collection('", $config:mods-root, "')//")
    )
};

(:~
    Generate an XPath query string from the given XML representation
    of the query.
:)
declare function biblio:generate-query($xml as element()) as xs:string* {
    typeswitch ($xml)
        case element(query) return
            for $child in $xml/*
            return
                biblio:generate-query($child)
        case element(and) return
            (
                biblio:generate-query($xml/*[1]),
                " intersect ",
                biblio:generate-query($xml/*[2])
            )
        case element(or) return
            (
                biblio:generate-query($xml/*[1]),
                " union ",
                biblio:generate-query($xml/*[2])
            )
        case element(not) return
            (
                biblio:generate-query($xml/*[1]),
                " except ",
                biblio:generate-query($xml/*[2])
            )
        case element(field) return
            let $expr0 := $biblio:FIELDS/field[@name = $xml/@name]
            let $expr := if ($expr0) then $expr0 else $biblio:FIELDS/field[last()]
            let $log := util:log("DEBUG", ("$expr0: ", $expr0))
            let $collection-path := $xml/ancestor::query/collection/string()

            let $collection := if($collection-path eq $sharing:groups-collection or $collection-path eq fn:replace($sharing:groups-collection, "/db/", ""))then
            (
                let $group-ids := sharing:get-users-groups(security:get-user-credential-from-session()[1])/@id return
                    if(not(empty($group-ids)))then
                    (
                        let $group-collections := for $group-id in $group-ids return
                            sharing:find-group-collections($group-id)
                        return
                            local:create-group-collections-string($group-ids, $group-collections)
                    )
                    else
                    (
                         util:log("debug", "biblio: could not find any shared collections for this user"),
                         fn:concat("collection('", $config:mods-root, "')//")
                    )
            )
            else if(fn:starts-with($collection-path, $sharing:groups-collection) or fn:starts-with($collection-path, fn:replace($sharing:groups-collection, "/db/", "")))then
            (
                (: search inside a group of shared collections :)
                 let $group-id := fn:replace($collection-path, ".*/", ""),
                 $group-collections := sharing:find-group-collections($group-id) return
                    local:create-group-collections-string($group-id, $group-collections)
            )
            else
            (
                (: search a single collection :)
                fn:concat("collection('", $collection-path, "')//")
            )
            return
                ($collection, replace($expr, '\$q', $xml/string()))
        case element(collection) return
            if (not($xml/..//field)) then
                ('collection("', $xml/string(), '")//mods:mods')
            else
                ()
        default return
            ()
};

(:~
    Transform the XML representation of the query into a simple string
    for display to the user in the query history.
:)
declare function biblio:xml-query-to-string($xml as element()) as xs:string* {
    typeswitch ($xml)
        case element(query) return
            for $query-term in $xml/*
                return biblio:xml-query-to-string($query-term)
        case element(and) return
            (
                biblio:xml-query-to-string($xml/*[1]),
                " AND ",
                biblio:xml-query-to-string($xml/*[2])
            )
        case element(or) return
            (
                biblio:xml-query-to-string($xml/*[1]),
                " OR ",
                biblio:xml-query-to-string($xml/*[2])
            )
        case element(not) return
            (
                biblio:xml-query-to-string($xml/*[1]),
                " NOT ",
                biblio:xml-query-to-string($xml/*[2])
            )
        case element(collection) return
            fn:concat("collection(""", $xml, """):")
        case element(field) return
            concat($xml/@name, ':', $xml/string())
        default return
            ()
};

(:~
    Process single form parameter. Called from biblio:process-form().
:)
declare function biblio:process-form-parameters($params as xs:string*) as element() {
    let $param := $params[1]
    let $n := substring-after($param, 'input')
    let $value := request:get-parameter($param, "")
    let $field := request:get-parameter(concat("field", $n), 'all')
    let $operator := request:get-parameter(concat("operator", $n), "and")
    return
        if (count($params) eq 1) then
            <field m="{$n}" name="{$field}">{$value}</field>
        else
            element { xs:QName($operator) } {
                biblio:process-form-parameters(subsequence($params, 2)),
                <field m="{$n}" name="{$field}">{$value}</field>
            }
};

(:~
    Process the received form parameters and create an XML representation of
    the query. Filter out empty parameters and take care of boolean operators.
:)
declare function biblio:process-form() as element(query)? {
    let $collection := request:get-parameter("collection", $config:mods-root)
    let $fields :=
        (:  Get a list of all input parameters which are not empty,
            ordered by input name. :)
        for $param in request:get-parameter-names()[starts-with(., 'input')]
        let $value := request:get-parameter($param, ())
        where string-length($value) gt 0
        order by $param descending
        return
            $param
    return
        if (exists($fields)) then
            (:  process-form recursively calls itself for every parameter and
                generates and XML representation of the query. :)
            <query>
                <collection>{$collection}</collection>
                { biblio:process-form-parameters($fields) }
            </query>
        else
            <query>
                <collection>{$collection}</collection>
            </query>
};

(:~
    Helper function used to sort by name within the "order by"
    clause of the query.
:)
declare function biblio:orderByName($m as element()) as xs:string?
{
    for $name in $m/mods:name[1]
    return
        mods:retrieve-name($name, 1)
};

(: Map order parameter to xpath for order by clause :)
declare function biblio:orderExpr($field as xs:string?) as xs:string
{
    if (sort:has-index('mods:name')) then
        if ($field eq "Score") then
            "ft:score($hit) descending"
        else if ($field = "Author") then
            "sort:index('mods:name', $hit)"
        else if ($field = "Title") then
            "sort:index('mods:title', $hit)"
        else
            "sort:index('mods:date', $hit)"
    else
        if ($field eq "Score") then
            "ft:score($hit) descending"
        else if ($field = "Author") then
            "biblio:orderByName($hit)"
        else if ($field = "Title") then
            "$hit/mods:titleInfo[1]/mods:title[1]"
        else
            "$hit/mods:originInfo/mods:dateCreated[1] descending"
};

(:~
    Evaluate the actual XPath query and order the results
:)
declare function biblio:evaluate-query($query as xs:string, $sort as xs:string?) {
    let $sortExpr := biblio:orderExpr($sort)
    let $orderedQuery :=
            concat("for $hit in ", $query, " order by ", $sortExpr, " return $hit")
    let $options :=
        <options>
            <default-operator>and</default-operator>
        </options>
    return
        util:eval($orderedQuery)
};

(:~
    Add a query to the user's query history. We store the XML representation
    of the query.
:)
declare function biblio:add-to-history($queryAsXML as element()) {
    let $oldHistory := session:get-attribute('history')
    let $newHistory :=
        let $n := if ($oldHistory) then max(for $n in $oldHistory/query/@n return xs:int($n)) + 1 else 1
        return
            <history>
                <query id="q{$n}" n="{$n}">
                    { $queryAsXML/* }
                </query>
                { $oldHistory/query }
            </history>
    return
        session:set-attribute('history', $newHistory)
};

(:~
    Retrieve a query from the query history
:)
declare function biblio:query-from-history($id as xs:string) {
    let $history := session:get-attribute('history')
    return
        $history/query[@id = $id]
};

(:~
    Returns the query history as a HTML list. The queries are
    transformed into a simple string representation.
:)
declare function biblio:query-history() {

    {
        let $history := session:get-attribute('history')
        for $query at $pos in $history/query
        return

{biblio:xml-query-to-string($query)}

    }

};

(:~
    Evaluate the query given as XML and store its results into the HTTP session
    for later reference.
:)
declare function biblio:eval-query($queryAsXML as element()?) {
    if ($queryAsXML) then
        let $query := string-join(biblio:generate-query($queryAsXML), '')
        let $log := util:log("DEBUG", ("QUERY: ", $query))
        let $sort0 := request:get-parameter("sort", ())
        let $sort := if ($sort0) then $sort0 else session:get-attribute("sort")
        let $results := biblio:evaluate-query($query, $sort)
        (:~ Take the query results and store them into the HTTP session. :)
        let $null := session:set-attribute('mods:cached', $results)
        let $null := session:set-attribute('query', $queryAsXML)
        let $null := session:set-attribute('sort', $queryAsXML)
        let $null := biblio:add-to-history($queryAsXML)
        return
            count($results)
    else
        0
};

(:~
    Clear the last query result.
:)
declare function biblio:clear() {
    let $null := session:remove-attribute('mods:cached')
    let $null := session:remove-attribute('query')
    let $null := session:remove-attribute('sort')
    return
        ()
};

(:~
    Scan the input HTML template and expand the biblio:* tags
    found therein.
:)
declare function biblio:process-templates($query as element()?, $hitCount as xs:integer?, $node as node()) {
    typeswitch ($node)
        case element(biblio:login) return
            let $user := request:get-attribute("xquery.user")
            return (
                if ($user eq 'guest') then

Login

                else

Logged in as {$user}. Logout

            )
        case element(biblio:form-from-query) return
            biblio:form-from-query($query)
        case element(biblio:result-count) return
            text { $hitCount }
        case element(biblio:query-history) return
            biblio:query-history()
        case element(biblio:collection-path) return
            let $collection := functx:replace-first(request:get-parameter("collection", $config:mods-root), "/db/", "")
            return
                <input class="collection-input" type="text" name="collection" value="{$collection}" readonly="true"/>
        case element(biblio:form-select-current-user-groups) return
            biblio:form-select-current-user-groups($node/@name)
        case element(biblio:current-user) return
            {request:get-attribute("xquery.user")}
        case element(biblio:conditional) return
            let $result := biblio:conditional($node) return
                if($result)then
                (
                    for $child in $result/node() return
                        biblio:process-templates($query, $hitCount, $child)
                )
                else()
        case element(biblio:is-collection-owner) return
            security:is-collection-owner(security:get-user-credential-from-session()[1], request:get-parameter("collection", $config:mods-root))
        case element(biblio:has-collection-write-permissions) return
            security:can-write-collection(security:get-user-credential-from-session()[1], request:get-parameter("collection", $config:mods-root))
        case element(biblio:form-select-collection) return
            biblio:form-select-collection($node/@name)
        case element(biblio:form-collection-sharing) return
            biblio:form-collection-sharing(request:get-parameter("collection", $config:mods-root))
        case element(biblio:form-add-member-to-sharing-group) return
            biblio:form-add-member-to-sharing-group()
        case element(biblio:form-add-sharing-group) return
            biblio:form-add-sharing-group()
        case element() return
            element { node-name($node) } {
                $node/@*,
                for $child in $node/node() return
                    biblio:process-templates($query, $hitCount, $child)
            }
        default return
            $node
};

declare function biblio:form-select-current-user-groups($select-name as xs:string) as element(select) {
    let $user := request:get-attribute("xquery.user") return
        <select name="{$select-name}">
        {
            for $group in xmldb:get-user-groups($user) return
                <option value="{$group}">{$group}</option>
        }
        </select>
};

declare function biblio:form-select-collection($select-name as xs:string) as element(select) {

    let $current-collection := request:get-parameter("collection", $config:mods-root) return

        <select name="{$select-name}">
        {
            if(security:can-write-collection(security:get-user-credential-from-session()[1], "/db"))then
            (
                element option {
                    if("/db" eq $current-collection)then
                    (
                        attribute selected { "selected" }
                    )else(),
                    text { "/db" }
                }
            )else(),

            for $sub-collection-path in biblio:get-writeable-subcollection-paths("/db") return
                element option {
                    if($sub-collection-path eq $current-collection)then
                    (
                        attribute selected { "selected" }
                    )else(),
                    text { $sub-collection-path }
                }
        }
        </select>
};

declare function biblio:form-add-member-to-sharing-group() {
    <jquery:dialog id="add-member-to-group-sharing-dialog" modal="true" title="Add User to Group" width="200">
        <select id="members-list">
        {
            for $user in security:get-other-biblio-users() return
                <option value="{$user}">{$user}</option>
        }
        </select>
        <input id="add-member-to-group-button" type="button" value="Add"/>
    </jquery:dialog>
};

declare function biblio:form-add-sharing-group() {
    <jquery:dialog id="new-group-sharing-dialog" modal="true" title="New Group" width="200">
        <input type="text" id="new-group-name"/>
        <input id="add-group-button" type="button" value="Add"/>
    </jquery:dialog>
};

declare function biblio:form-collection-sharing($collection as xs:string) {
    <jquery:dialog id="sharing-collection-dialog" modal="true" title="Collection Sharing" trigger="#collection-sharing" width="450">
        <jquery:button label="Update" function="updateCollectionSharing"/>
        <jquery:button id="cancel" label="Cancel"/>
        <form id="update-collection-sharing-form" action="operations.xql">
            <input id="sharing-collection-path_" type="hidden" name="collection"/>

Sharing settings for:

                <input type="checkbox" id="sharing-collection-with-group" name="sharing-collection-with" value="group">
                {
                 (:   if(sharing:group-readable($collection))then(attribute checked{ "checked" })else()
                 :)
                 ()
                }
                </input>
                Share with Group
                {
                let $groups := sharing:get-groups(),
                $collection-group-id := sharing:get-group-id($collection) return

                            Group
                            <select id="group-list" name="group">
                            {
                                for $group in $groups return
                                    if($group/@id eq $collection-group-id)then
                                    (
                                        <option selected="selected" value="{$group/@id}">{$group/group:name/text()}</option>
                                    )
                                    else
                                    (
                                        <option value="{$group/@id}">{$group/group:name/text()}</option>
                                    )
                            }
                            </select>
                            <input id="new-group-button" type="button" value="New Group"/>

                                Members
                                <ui id="group-members-list">
                                {
                                    (: get members of the selected group, or if none selected then the first group :)
                                    let $selected-group := if(not(empty($groups[@id eq $collection-group-id])))then($groups[@id eq $collection-group-id])else($groups[1]) return
                                        if($selected-group)then
                                        (
                                            for $group-member at $i in sharing:get-group-members($selected-group/@id) return

                                                <input id="group-member-{$i}" type="checkbox" name="group-member" value="{$group-member}" checked="checked"/>
                                                {$group-member}

                                        )else()
                                }
                                </ui>

                            <input id="add-new-member-to-group-button" type="button" value="Add Member"/>

                                <input id="group-sharing-premissions-read" type="checkbox" name="group-sharing-permissions" value="read">
                                {
                                (:
                                    if(sharing:group-readable($collection))then(attribute checked{ "checked" })else()
                                    :)
                                    ()
                                }
                                </input>
                                Read

                                <input id="group-sharing-premissions-write" type="checkbox" name="group-sharing-permissions" value="write">
                                {
                                    (:
                                    if(sharing:group-writeable($collection))then(attribute checked{ "checked" })else()
                                    :)
                                    ()
                                }
                                </input>
                                Write

            }

                <input type="checkbox" id="sharing-collection-with-other" name="sharing-collection-with" value="other">
                {
                    (:
                    if(sharing:other-readable($collection))then(attribute checked{ "checked" })else()
                    :)
                    ()
                }
                </input>
                Share with Everyone

                            <input id="other-sharing-premissions-read" type="checkbox" name="other-sharing-permissions" value="read">
                            {
                                (:
                                if(sharing:other-readable($collection))then(attribute checked{ "checked" })else()
                                :)
                                ()
                            }
                            </input>
                            Read

                            <input id="other-sharing-premissions-write" type="checkbox" name="other-sharing-permissions" value="write">
                            {
                                (:
                                if(sharing:other-writeable($collection))then(attribute checked{ "checked" })else()
                                :)
                                ()
                            }
                            </input>
                            Write

        </form>
    </jquery:dialog>
};

declare function biblio:get-writeable-subcollection-paths($path as xs:string) {

	for $sub in xmldb:get-child-collections($path)
	let $col := concat($path, "/", $sub) return
		(
			if(security:can-write-collection(security:get-user-credential-from-session()[1], $col))then
			(
			 $col
			)else(),
			biblio:get-writeable-subcollection-paths($col)
		)
};

declare function biblio:conditional($node as element(biblio:conditional)) {
    let $test-result := biblio:process-templates((), (), $node/biblio:test/biblio:*) return
        if($test-result) then
        (
            $node/biblio:result
        )else()
};

(:~
    Filter an existing result set by applying an additional
    clause with "and".
:)
declare function biblio:apply-filter() {
    let $prevQuery := session:get-attribute("query")
    let $filter := request:get-parameter("filter", ())
    let $value := request:get-parameter("value", ())

    return
        if (not($prevQuery/field)) then
            <query>
                { $prevQuery/collection }
                <field name="{$filter}">{$value}</field>
            </query>
        else
            <query>
                { $prevQuery/collection }
                <and>
                { $prevQuery/*[not(self::collection)] }
                <field name="{$filter}">{$value}</field>
                </and>
            </query>
};

session:create(),
(: We receive an HTML template as input :)
let $input := request:get-data()
let $filter := request:get-parameter("filter", ())
let $history := request:get-parameter("history", ())
let $clear := request:get-parameter("clear", ())
let $mylist := request:get-parameter("mylist", ())
let $collection := request:get-parameter("collection", ())

(: Process request parameters and generate an XML representation of the query :)
let $queryAsXML :=
    if ($history) then
        biblio:query-from-history($history)
    else if ($clear) then
        biblio:clear()
    else if ($filter) then
        biblio:apply-filter()
    else if ($mylist eq 'display') then
        ()
    else
        biblio:process-form()
let $log := util:log("DEBUG", ("$queryAsXML: ", $queryAsXML))
(: Evaluate the query :)
let $results :=
    if ($mylist) then (
        if ($mylist eq 'clear') then
            session:set-attribute("personal-list", ())
        else
            (),
        let $list := session:get-attribute("personal-list")
        let $items :=
            for $item in $list/listitem
            return
                util:node-by-id(doc(substring-before($item/@id, '#')), substring-after($item/@id, '#'))
        let $null := session:set-attribute('mods:cached', $items)
        return
            count($items)
    ) else
        biblio:eval-query($queryAsXML)
(:  Process the HTML template received as input :)
let $output :=
    jquery:process(
        biblio:process-templates(if ($queryAsXML//field) then $queryAsXML else $biblio:TEMPLATE_QUERY, $results, $input)
    )
return
    $output

xquery version "1.0";

(:~
    The core XQuery script for the bibliographic demo. It receives a template XML document
    from the controller and expands it. If a search was trigger by the user, the script
    proceeds as follows:

the input form parameters are transformed into a simple XML structure
            to describe the query

an XPath string is generated from the XML query structure

the XPath is executed and the sort criteria applied

query results, XML query and sort criteria are stored into the HTTP session

the template is expanded, forms are regenerated to match the query

    To apply a filter to an existing query, we just extend the XML representation
    of the query.
:)
declare namespace biblio="http:/exist-db.org/xquery/biblio";
declare namespace group="http://exist-db.org/mods/sharing/group";
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 xmldb="http://exist-db.org/xquery/xmldb";
declare namespace functx = "http://www.functx.com";

import module namespace config="http://exist-db.org/mods/config" at "config.xqm";
import module namespace jquery="http://exist-db.org/xquery/jquery" at "resource:org/exist/xquery/lib/jquery.xql";

import module namespace mods="http://www.loc.gov/mods/v3" at "retrieve-mods.xql";
import module namespace sort="http://exist-db.org/xquery/sort" at "java:org.exist.xquery.modules.sort.SortModule";
import module namespace security="http://exist-db.org/mods/security" at "security.xqm";
import module namespace sharing="http://exist-db.org/mods/sharing" at "sharing.xqm";

declare option exist:serialize "method=xhtml media-type=application/xhtml+xml omit-xml-declaration=no enforce-xhtml=yes";

declare function functx:replace-first( $arg as xs:string?, $pattern as xs:string, $replacement as xs:string )  as xs:string {
   replace($arg, concat('(^.*?)', $pattern),
             concat('$1',$replacement))
 } ;

(:~
    Simple mapping from field names to an XPath expression
:)
declare variable $biblio:FIELDS :=
	<fields>
		<field name="Title">(
		mods:mods[ft:query(mods:titleInfo, '$q', $options)]
		      union
		mods:mods[ft:query(mods:relatedItem/mods:titleInfo, '$q', $options)]
		)</field>
		<field name="Name">(
		mods:mods[ft:query(mods:name, '$q', $options)]
		      union
		mods:mods[ft:query(mods:relatedItem/mods:name, '$q', $options)]
		)</field>
		<field name="Date">(
		mods:mods[ft:query(mods:originInfo/mods:dateCreated, '$q', $options)]
		      union
		mods:mods[ft:query(mods:originInfo/mods:dateIssued, '$q', $options)]
		      union
		mods:mods[ft:query(mods:originInfo/mods:dateCaptured, '$q', $options)]
		      union
		mods:mods[ft:query(mods:originInfo/mods:copyrightDate, '$q', $options)]
		      union
		mods:mods[ft:query(mods:relatedItem/mods:originInfo/mods:dateCreated, '$q', $options)]
		      union
		mods:mods[ft:query(mods:relatedItem/mods:originInfo/mods:dateIssued, '$q', $options)]
		      union
		mods:mods[ft:query(mods:relatedItem/mods:originInfo/mods:dateCaptured, '$q', $options)]
		      union
		mods:mods[ft:query(mods:relatedItem/mods:originInfo/mods:copyrightDate, '$q', $options)]
		)</field>
		<field name="Abstract">mods:mods[ft:query(mods:abstract, '$q', $options)]</field>
        <field name="Note">mods:mods[ft:query(mods:note, '$q', $options)]</field>
        <field name="Subject">mods:mods[ft:query(mods:subject, '$q', $options)]</field>
        <field name="All">(
        mods:mods[ft:query(.//*, '$q', $options)]
		)</field>

	</fields>;

(:
    Default template to be used for form generation if no
    query was specified
:)
declare variable $biblio:TEMPLATE_QUERY :=
    <query>
        <collection>{$config:mods-root}</collection>
        <and>
            <field m="1" name="All"></field>
        </and>
    </query>;

(:~
    Regenerate the HTML form to match the query, e.g. after adding more filter
    clauses.
:)
declare function biblio:form-from-query($query as element()) as element()+ {
    for $field at $pos in $query//field
    return

            {
                let $operator :=
                    if ($field/preceding-sibling::*) then
                        $field/../local-name(.)
                    else
                        ()
                return
                    <select name="operator{$pos}">
                    { if (empty($operator)) then attribute style { "display: none;" } else () }
                    {
                        for $opt in ("and", "or", "not")
                        return
                            <option>
                            {
                                if ($opt eq $operator) then
                                    attribute selected { "selected" }
                                else
                                    ()
                            }
                            { $opt }
                            </option>
                    }
                    </select>
            }

                <jquery:input name="input{$pos}" value="{$field/string()}">
                    <jquery:autocomplete url="autocomplete.xql"
                        width="300" multiple="false"
                        matchContains="false"
                        paramsCallback="autocompleteCallback">
                    </jquery:autocomplete>
                </jquery:input>

                in <select name="field{$pos}">
                {
                    for $f in $biblio:FIELDS/field
                    return
                        <option>
                            { if ($f/@name eq $field/@name) then attribute selected { "selected" } else () }
                            {$f/@name/string()}
                        </option>
                }
                </select>

};

declare function local:create-group-collections-string($group-ids as xs:string+,  $group-collections as xs:string*) as xs:string*
{
    if(not(empty($group-collections)))then(
        fn:concat(
            "collection(",
            fn:string-join(
                for $shared-collection in $group-collections return
                    fn:concat("'", $shared-collection, "'")
            , ","),
            ")//"
        )
    )
    else
    (
        util:log("debug", fn:concat("biblio: could not find any shared collections for the group with id: ", $group-ids)),
        fn:concat("collection('", $config:mods-root, "')//")
    )
};

(:~
    Generate an XPath query string from the given XML representation
    of the query.
:)
declare function biblio:generate-query($xml as element()) as xs:string* {
    typeswitch ($xml)
        case element(query) return
            for $child in $xml/*
            return
                biblio:generate-query($child)
        case element(and) return
            (
                biblio:generate-query($xml/*[1]),
                " intersect ",
                biblio:generate-query($xml/*[2])
            )
        case element(or) return
            (
                biblio:generate-query($xml/*[1]),
                " union ",
                biblio:generate-query($xml/*[2])
            )
        case element(not) return
            (
                biblio:generate-query($xml/*[1]),
                " except ",
                biblio:generate-query($xml/*[2])
            )
        case element(field) return
            let $expr0 := $biblio:FIELDS/field[@name = $xml/@name]
            let $expr := if ($expr0) then $expr0 else $biblio:FIELDS/field[last()]
            let $log := util:log("DEBUG", ("$expr0: ", $expr0))
            let $collection-path := $xml/ancestor::query/collection/string()

            let $collection := if($collection-path eq $sharing:groups-collection)then
            (
                let $group-ids := sharing:get-users-groups(security:get-user-credential-from-session()[1])/@id return
                    if(not(empty($group-ids)))then
                    (
                        let $group-collections := for $group-id in $group-ids return
                            sharing:find-group-collections($group-id)
                        return
                            local:create-group-collections-string($group-ids, $group-collections)
                    )
                    else
                    (
                         util:log("debug", "biblio: could not find any shared collections for this user"),
                         fn:concat("collection('", $config:mods-root, "')//")
                    )
            )
            else if(fn:starts-with($collection-path, $sharing:groups-collection) or fn:starts-with($collection-path, fn:replace($sharing:groups-collection, "/db/", "")))then
            (
                (: search inside a group of shared collections :)
                 let $group-id := fn:replace($collection-path, ".*/", ""),
                 $group-collections := sharing:find-group-collections($group-id) return
                    local:create-group-collections-string($group-id, $group-collections)
            )
            else
            (
                (: search a single collection :)
                fn:concat("collection('", $collection-path, "')//")
            )
            return
                ($collection, replace($expr, '\$q', $xml/string()))
        case element(collection) return
            if (not($xml/..//field)) then
                ('collection("', $xml/string(), '")//mods:mods')
            else
                ()
        default return
            ()
};

(:~
    Transform the XML representation of the query into a simple string
    for display to the user in the query history.
:)
declare function biblio:xml-query-to-string($xml as element()) as xs:string* {
    typeswitch ($xml)
        case element(query) return
            for $query-term in $xml/*
                return biblio:xml-query-to-string($query-term)
        case element(and) return
            (
                biblio:xml-query-to-string($xml/*[1]),
                " AND ",
                biblio:xml-query-to-string($xml/*[2])
            )
        case element(or) return
            (
                biblio:xml-query-to-string($xml/*[1]),
                " OR ",
                biblio:xml-query-to-string($xml/*[2])
            )
        case element(not) return
            (
                biblio:xml-query-to-string($xml/*[1]),
                " NOT ",
                biblio:xml-query-to-string($xml/*[2])
            )
        case element(collection) return
            fn:concat("collection(""", $xml, """):")
        case element(field) return
            concat($xml/@name, ':', $xml/string())
        default return
            ()
};

(:~
    Process single form parameter. Called from biblio:process-form().
:)
declare function biblio:process-form-parameters($params as xs:string*) as element() {
    let $param := $params[1]
    let $n := substring-after($param, 'input')
    let $value := request:get-parameter($param, "")
    let $field := request:get-parameter(concat("field", $n), 'all')
    let $operator := request:get-parameter(concat("operator", $n), "and")
    return
        if (count($params) eq 1) then
            <field m="{$n}" name="{$field}">{$value}</field>
        else
            element { xs:QName($operator) } {
                biblio:process-form-parameters(subsequence($params, 2)),
                <field m="{$n}" name="{$field}">{$value}</field>
            }
};

(:~
    Process the received form parameters and create an XML representation of
    the query. Filter out empty parameters and take care of boolean operators.
:)
declare function biblio:process-form() as element(query)? {
    let $collection := request:get-parameter("collection", $config:mods-root)
    let $fields :=
        (:  Get a list of all input parameters which are not empty,
            ordered by input name. :)
        for $param in request:get-parameter-names()[starts-with(., 'input')]
        let $value := request:get-parameter($param, ())
        where string-length($value) gt 0
        order by $param descending
        return
            $param
    return
        if (exists($fields)) then
            (:  process-form recursively calls itself for every parameter and
                generates and XML representation of the query. :)
            <query>
                <collection>{$collection}</collection>
                { biblio:process-form-parameters($fields) }
            </query>
        else
            <query>
                <collection>{$collection}</collection>
            </query>
};

(:~
    Helper function used to sort by name within the "order by"
    clause of the query.
:)
declare function biblio:orderByName($m as element()) as xs:string?
{
    for $name in $m/mods:name[1]
    return
        mods:retrieve-name($name, 1)
};

(: Map order parameter to xpath for order by clause :)
declare function biblio:orderExpr($field as xs:string?) as xs:string
{
    if (sort:has-index('mods:name')) then
        if ($field eq "Score") then
            "ft:score($hit) descending"
        else if ($field = "Author") then
            "sort:index('mods:name', $hit)"
        else if ($field = "Title") then
            "sort:index('mods:title', $hit)"
        else
            "sort:index('mods:date', $hit)"
    else
        if ($field eq "Score") then
            "ft:score($hit) descending"
        else if ($field = "Author") then
            "biblio:orderByName($hit)"
        else if ($field = "Title") then
            "$hit/mods:titleInfo[1]/mods:title[1]"
        else
            "$hit/mods:originInfo/mods:dateCreated[1] descending"
};

(:~
    Evaluate the actual XPath query and order the results
:)
declare function biblio:evaluate-query($query as xs:string, $sort as xs:string?) {
    let $sortExpr := biblio:orderExpr($sort)
    let $orderedQuery :=
            concat("for $hit in ", $query, " order by ", $sortExpr, " return $hit")
    let $options :=
        <options>
            <default-operator>and</default-operator>
        </options>
    return
        util:eval($orderedQuery)
};

(:~
    Add a query to the user's query history. We store the XML representation
    of the query.
:)
declare function biblio:add-to-history($queryAsXML as element()) {
    let $oldHistory := session:get-attribute('history')
    let $newHistory :=
        let $n := if ($oldHistory) then max(for $n in $oldHistory/query/@n return xs:int($n)) + 1 else 1
        return
            <history>
                <query id="q{$n}" n="{$n}">
                    { $queryAsXML/* }
                </query>
                { $oldHistory/query }
            </history>
    return
        session:set-attribute('history', $newHistory)
};

(:~
    Retrieve a query from the query history
:)
declare function biblio:query-from-history($id as xs:string) {
    let $history := session:get-attribute('history')
    return
        $history/query[@id = $id]
};

(:~
    Returns the query history as a HTML list. The queries are
    transformed into a simple string representation.
:)
declare function biblio:query-history() {

    {
        let $history := session:get-attribute('history')
        for $query at $pos in $history/query
        return

{biblio:xml-query-to-string($query)}

    }

};

(:~
    Evaluate the query given as XML and store its results into the HTTP session
    for later reference.
:)
declare function biblio:eval-query($queryAsXML as element()?) {
    if ($queryAsXML) then
        let $query := string-join(biblio:generate-query($queryAsXML), '')
        let $log := util:log("DEBUG", ("QUERY: ", $query))
        let $sort0 := request:get-parameter("sort", ())
        let $sort := if ($sort0) then $sort0 else session:get-attribute("sort")
        let $results := biblio:evaluate-query($query, $sort)
        (:~ Take the query results and store them into the HTTP session. :)
        let $null := session:set-attribute('mods:cached', $results)
        let $null := session:set-attribute('query', $queryAsXML)
        let $null := session:set-attribute('sort', $queryAsXML)
        let $null := biblio:add-to-history($queryAsXML)
        return
            count($results)
    else
        0
};

(:~
    Clear the last query result.
:)
declare function biblio:clear() {
    let $null := session:remove-attribute('mods:cached')
    let $null := session:remove-attribute('query')
    let $null := session:remove-attribute('sort')
    return
        ()
};

(:~
    Scan the input HTML template and expand the biblio:* tags
    found therein.
:)
declare function biblio:process-templates($query as element()?, $hitCount as xs:integer?, $node as node()) {
    typeswitch ($node)
        case element(biblio:login) return
            let $user := request:get-attribute("xquery.user")
            return (
                if ($user eq 'guest') then

Login

                else

Logged in as {$user}. Logout

            )
        case element(biblio:form-from-query) return
            biblio:form-from-query($query)
        case element(biblio:result-count) return
            text { $hitCount }
        case element(biblio:query-history) return
            biblio:query-history()
        case element(biblio:collection-path) return
            let $collection := functx:replace-first(request:get-parameter("collection", $config:mods-root), "/db/", "")
            return
                <input class="collection-input" type="text" name="collection" value="{$collection}" readonly="true"/>
        case element(biblio:form-select-current-user-groups) return
            biblio:form-select-current-user-groups($node/@name)
        case element(biblio:current-user) return
            {request:get-attribute("xquery.user")}
        case element(biblio:conditional) return
            let $result := biblio:conditional($node) return
                if($result)then
                (
                    for $child in $result/node() return
                        biblio:process-templates($query, $hitCount, $child)
                )
                else()
        case element(biblio:is-collection-owner) return
            security:is-collection-owner(security:get-user-credential-from-session()[1], request:get-parameter("collection", $config:mods-root))
        case element(biblio:has-collection-write-permissions) return
            security:can-write-collection(security:get-user-credential-from-session()[1], request:get-parameter("collection", $config:mods-root))
        case element(biblio:form-select-collection) return
            biblio:form-select-collection($node/@name)
        case element(biblio:form-collection-sharing) return
            biblio:form-collection-sharing(request:get-parameter("collection", $config:mods-root))
        case element(biblio:form-add-member-to-sharing-group) return
            biblio:form-add-member-to-sharing-group()
        case element(biblio:form-add-sharing-group) return
            biblio:form-add-sharing-group()
        case element() return
            element { node-name($node) } {
                $node/@*,
                for $child in $node/node() return
                    biblio:process-templates($query, $hitCount, $child)
            }
        default return
            $node
};

declare function biblio:form-select-current-user-groups($select-name as xs:string) as element(select) {
    let $user := request:get-attribute("xquery.user") return
        <select name="{$select-name}">
        {
            for $group in xmldb:get-user-groups($user) return
                <option value="{$group}">{$group}</option>
        }
        </select>
};

declare function biblio:form-select-collection($select-name as xs:string) as element(select) {

    let $current-collection := request:get-parameter("collection", $config:mods-root) return

        <select name="{$select-name}">
        {
            if(security:can-write-collection(security:get-user-credential-from-session()[1], "/db"))then
            (
                element option {
                    if("/db" eq $current-collection)then
                    (
                        attribute selected { "selected" }
                    )else(),
                    text { "/db" }
                }
            )else(),

            for $sub-collection-path in biblio:get-writeable-subcollection-paths("/db") return
                element option {
                    if($sub-collection-path eq $current-collection)then
                    (
                        attribute selected { "selected" }
                    )else(),
                    text { $sub-collection-path }
                }
        }
        </select>
};

declare function biblio:form-add-member-to-sharing-group() {
    <jquery:dialog id="add-member-to-group-sharing-dialog" modal="true" title="Add User to Group" width="200">
        <select id="members-list">
        {
            for $user in security:get-other-biblio-users() return
                <option value="{$user}">{$user}</option>
        }
        </select>
        <input id="add-member-to-group-button" type="button" value="Add"/>
    </jquery:dialog>
};

declare function biblio:form-add-sharing-group() {
    <jquery:dialog id="new-group-sharing-dialog" modal="true" title="New Group" width="200">
        <input type="text" id="new-group-name"/>
        <input id="add-group-button" type="button" value="Add"/>
    </jquery:dialog>
};

declare function biblio:form-collection-sharing($collection as xs:string) {
    <jquery:dialog id="sharing-collection-dialog" modal="true" title="Collection Sharing" trigger="#collection-sharing" width="450">
        <jquery:button label="Update" function="updateCollectionSharing"/>
        <jquery:button id="cancel" label="Cancel"/>
        <form id="update-collection-sharing-form" action="operations.xql">
            <input id="sharing-collection-path_" type="hidden" name="collection"/>

Sharing settings for:

                <input type="checkbox" id="sharing-collection-with-group" name="sharing-collection-with" value="group">
                {
                 (:   if(sharing:group-readable($collection))then(attribute checked{ "checked" })else()
                 :)
                 ()
                }
                </input>
                Share with Group
                {
                let $groups := sharing:get-groups(),
                $collection-group-id := sharing:get-group-id($collection) return

                            Group
                            <select id="group-list" name="group">
                            {
                                for $group in $groups return
                                    if($group/@id eq $collection-group-id)then
                                    (
                                        <option selected="selected" value="{$group/@id}">{$group/group:name/text()}</option>
                                    )
                                    else
                                    (
                                        <option value="{$group/@id}">{$group/group:name/text()}</option>
                                    )
                            }
                            </select>
                            <input id="new-group-button" type="button" value="New Group"/>

                                Members
                                <ui id="group-members-list">
                                {
                                    (: get members of the selected group, or if none selected then the first group :)
                                    let $selected-group := if(not(empty($groups[@id eq $collection-group-id])))then($groups[@id eq $collection-group-id])else($groups[1]) return
                                        if($selected-group)then
                                        (
                                            for $group-member at $i in sharing:get-group-members($selected-group/@id) return

                                                <input id="group-member-{$i}" type="checkbox" name="group-member" value="{$group-member}" checked="checked"/>
                                                {$group-member}

                                        )else()
                                }
                                </ui>

                            <input id="add-new-member-to-group-button" type="button" value="Add Member"/>

                                <input id="group-sharing-premissions-read" type="checkbox" name="group-sharing-permissions" value="read">
                                {
                                (:
                                    if(sharing:group-readable($collection))then(attribute checked{ "checked" })else()
                                    :)
                                    ()
                                }
                                </input>
                                Read

                                <input id="group-sharing-premissions-write" type="checkbox" name="group-sharing-permissions" value="write">
                                {
                                    (:
                                    if(sharing:group-writeable($collection))then(attribute checked{ "checked" })else()
                                    :)
                                    ()
                                }
                                </input>
                                Write

            }

                <input type="checkbox" id="sharing-collection-with-other" name="sharing-collection-with" value="other">
                {
                    (:
                    if(sharing:other-readable($collection))then(attribute checked{ "checked" })else()
                    :)
                    ()
                }
                </input>
                Share with Everyone

                            <input id="other-sharing-premissions-read" type="checkbox" name="other-sharing-permissions" value="read">
                            {
                                (:
                                if(sharing:other-readable($collection))then(attribute checked{ "checked" })else()
                                :)
                                ()
                            }
                            </input>
                            Read

                            <input id="other-sharing-premissions-write" type="checkbox" name="other-sharing-permissions" value="write">
                            {
                                (:
                                if(sharing:other-writeable($collection))then(attribute checked{ "checked" })else()
                                :)
                                ()
                            }
                            </input>
                            Write

        </form>
    </jquery:dialog>
};

declare function biblio:get-writeable-subcollection-paths($path as xs:string) {

	for $sub in xmldb:get-child-collections($path)
	let $col := concat($path, "/", $sub) return
		(
			if(security:can-write-collection(security:get-user-credential-from-session()[1], $col))then
			(
			 $col
			)else(),
			biblio:get-writeable-subcollection-paths($col)
		)
};

declare function biblio:conditional($node as element(biblio:conditional)) {
    let $test-result := biblio:process-templates((), (), $node/biblio:test/biblio:*) return
        if($test-result) then
        (
            $node/biblio:result
        )else()
};

(:~
    Filter an existing result set by applying an additional
    clause with "and".
:)
declare function biblio:apply-filter() {
    let $prevQuery := session:get-attribute("query")
    let $filter := request:get-parameter("filter", ())
    let $value := request:get-parameter("value", ())

    return
        if (not($prevQuery/field)) then
            <query>
                { $prevQuery/collection }
                <field name="{$filter}">{$value}</field>
            </query>
        else
            <query>
                { $prevQuery/collection }
                <and>
                { $prevQuery/*[not(self::collection)] }
                <field name="{$filter}">{$value}</field>
                </and>
            </query>
};

session:create(),
(: We receive an HTML template as input :)
let $input := request:get-data()
let $filter := request:get-parameter("filter", ())
let $history := request:get-parameter("history", ())
let $clear := request:get-parameter("clear", ())
let $mylist := request:get-parameter("mylist", ())
let $collection := request:get-parameter("collection", ())

(: Process request parameters and generate an XML representation of the query :)
let $queryAsXML :=
    if ($history) then
        biblio:query-from-history($history)
    else if ($clear) then
        biblio:clear()
    else if ($filter) then
        biblio:apply-filter()
    else if ($mylist eq 'display') then
        ()
    else
        biblio:process-form()
let $log := util:log("DEBUG", ("$queryAsXML: ", $queryAsXML))
(: Evaluate the query :)
let $results :=
    if ($mylist) then (
        if ($mylist eq 'clear') then
            session:set-attribute("personal-list", ())
        else
            (),
        let $list := session:get-attribute("personal-list")
        let $items :=
            for $item in $list/listitem
            return
                util:node-by-id(doc(substring-before($item/@id, '#')), substring-after($item/@id, '#'))
        let $null := session:set-attribute('mods:cached', $items)
        return
            count($items)
    ) else
        biblio:eval-query($queryAsXML)
(:  Process the HTML template received as input :)
let $output :=
    jquery:process(
        biblio:process-templates(if ($queryAsXML//field) then $queryAsXML else $biblio:TEMPLATE_QUERY, $results, $input)
    )
return
    $output


Issue #335 instance xml from JSON documents

(:
 Copyright 2002-2017 MarkLogic Corporation.  All Rights Reserved.

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
:)
xquery version "1.0-ml";

module namespace inst = "http://marklogic.com/entity-services-instance";
declare namespace es = "http://marklogic.com/entity-services";
declare namespace tde = "http://marklogic.com/xdmp/tde";
declare namespace xq = "http://www.w3.org/2012/xquery";

import module namespace sem = "http://marklogic.com/semantics" at "/MarkLogic/semantics.xqy";
import module namespace json = "http://marklogic.com/xdmp/json" at "/MarkLogic/json/json.xqy";

declare default function namespace "http://www.w3.org/2005/xpath-functions";

(: declare option xdmp:mapping "false"; :)
declare option xq:require-feature "xdmp:three-one";

declare function inst:instance-from-document(
    $document as document-node()
) as map:map*
{
    let $xml-from-document := inst:instance-xml-from-document($document)
    for $root-instance in $xml-from-document
        return inst:child-instance($root-instance)
};

declare function inst:child-instance(
    $element as element()
) as map:map*
{
    if (empty($element/*) and exists($element/text()))
    then json:object()
            =>map:with("$type", local-name($element))
            =>map:with("$ref", $element/data())
    else
        let $child := json:object()=>map:with("$type", local-name($element))
        let $_ :=
            for $property in $element/*
            return
                if (map:contains($child, local-name($property)))
                then
                    if (map:get($child, local-name($property)) instance of json:array)
                    then json:array-push(map:get($child, local-name($property)),
                            if ($property/element())
                            then inst:child-instance($property/*)
                            else data($property))
                    else
                        let $new-array := json:array()
                        return (
                            json:array-push($new-array, map:get($child, local-name($property))),
                            json:array-push($new-array, inst:child-instance($property/*)),
                            map:put($child, local-name($property), $new-array)
                        )
                else
                    if ($property[@datatype eq "array"])
                    then
                        map:put($child, local-name($property),
                            json:array()
                            =>json:array-with(
                                if ($property/element())
                                then inst:child-instance($property/element())
                                else data($property))
                            )
                    else
                        map:put($child, local-name($property),
                            if ($property/element())
                            then inst:child-instance($property/element())
                            else data($property))
        return $child
};

declare function inst:instance-xml-from-document(
    $document as document-node()
) as element()*
{
    if ($document/element())
    then $document//es:instance/(* except es:info)
    else
        let $json := inst:instance-json-from-document($document)
        return json:transform-from-json($json,
            json:config("custom")=>map:with("element-namespace",""))

};

declare private function inst:wrap-instance(
    $instance as json:object
) as json:object
{
    let $type := map:get($instance, "$type")
    let $remove-it := map:delete($instance, "$type")
    return json:object()
        =>map:with($type,
            if (map:contains($instance, "$ref"))
            then map:get($instance, "$ref")
            else
                let $value-map := json:object()
                let $_ :=
                    for $k in map:keys($instance)
                    let $v := map:get($instance, $k)
                    return
                    typeswitch ($v)
                    case json:array return

                        if (json:array-size($v) eq 0)
                        then json:object()=>map:with($k, $v)
                        else if ($v[1] instance of json:object)
                        then map:put($value-map, $k, (json:to-array(json:array-values($v) ! inst:wrap-instance(.))))
                        else map:put($value-map, $k, $v)
                    case json:object return
                        map:put($value-map, $k, inst:wrap-instance($v))
                    default return map:put($value-map, $k, $v)
                return $value-map)
};

declare function inst:instance-json-from-document(
    $document as document-node()
) as object-node()*
{
    if ($document/object-node())
    then
        for $n in $document//instance/*
        where fn:node-name($n) ne xs:QName("info")
        return
        object-node { fn:node-name($n) : $n }
    else
    (inst:instance-from-document($document) !
        (inst:wrap-instance(.)=>xdmp:to-json()))/node()
};

declare function inst:instance-get-attachments(
    $document as document-node()
) as item()*
{
    if (exists($document//*:attachments/*))
    then $document//*:attachments/*
    else $document//*:attachments/text()
};

(:
 Copyright 2002-2017 MarkLogic Corporation.  All Rights Reserved.

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
:)
xquery version "1.0-ml";

module namespace inst = "http://marklogic.com/entity-services-instance";
declare namespace es = "http://marklogic.com/entity-services";
declare namespace tde = "http://marklogic.com/xdmp/tde";
declare namespace xq = "http://www.w3.org/2012/xquery";

import module namespace sem = "http://marklogic.com/semantics" at "/MarkLogic/semantics.xqy";

declare default function namespace "http://www.w3.org/2005/xpath-functions";

(: declare option xdmp:mapping "false"; :)
declare option xq:require-feature "xdmp:three-one";

declare function inst:instance-from-document(
    $document as document-node()
) as map:map*
{
    let $xml-from-document := inst:instance-xml-from-document($document)
    for $root-instance in $xml-from-document
        return inst:child-instance($root-instance)
};

declare function inst:child-instance(
    $element as element()
) as map:map*
{
    if (empty($element/*) and exists($element/text()))
    then json:object()
            =>map:with("$type", local-name($element))
            =>map:with("$ref", $element/data())
    else
        let $child := json:object()=>map:with("$type", local-name($element))
        let $_ :=
            for $property in $element/*
            return
                if (map:contains($child, local-name($property)))
                then
                    if (map:get($child, local-name($property)) instance of json:array)
                    then json:array-push(map:get($child, local-name($property)),
                            if ($property/element())
                            then inst:child-instance($property/*)
                            else data($property))
                    else
                        let $new-array := json:array()
                        return (
                            json:array-push($new-array, map:get($child, local-name($property))),
                            json:array-push($new-array, inst:child-instance($property/*)),
                            map:put($child, local-name($property), $new-array)
                        )
                else
                    if ($property[@datatype eq "array"])
                    then
                        map:put($child, local-name($property),
                            json:array()
                            =>json:array-with(
                                if ($property/element())
                                then inst:child-instance($property/element())
                                else data($property))
                            )
                    else
                        map:put($child, local-name($property),
                            if ($property/element())
                            then inst:child-instance($property/element())
                            else data($property))
        return $child
};

declare function inst:instance-xml-from-document(
    $document as document-node()
) as element()*
{
    $document//es:instance/(* except es:info)
};

declare private function inst:wrap-instance(
    $instance as json:object
) as json:object
{
    let $type := map:get($instance, "$type")
    let $remove-it := map:delete($instance, "$type")
    return json:object()
        =>map:with($type,
            if (map:contains($instance, "$ref"))
            then map:get($instance, "$ref")
            else
                let $value-map := json:object()
                let $_ :=
                    for $k in map:keys($instance)
                    let $v := map:get($instance, $k)
                    return
                    typeswitch ($v)
                    case json:array return

                        if (json:array-size($v) eq 0)
                        then json:object()=>map:with($k, $v)
                        else if ($v[1] instance of json:object)
                        then map:put($value-map, $k, (json:to-array(json:array-values($v) ! inst:wrap-instance(.))))
                        else map:put($value-map, $k, $v)
                    case json:object return
                        map:put($value-map, $k, inst:wrap-instance($v))
                    default return map:put($value-map, $k, $v)
                return $value-map)
};

declare function inst:instance-json-from-document(
    $document as document-node()
) as object-node()*
{
    if ($document/object-node())
    then
        for $n in $document//instance/*
        where fn:node-name($n) ne xs:QName("info")
        return
        object-node { fn:node-name($n) : $n }
    else
    (inst:instance-from-document($document) !
        (inst:wrap-instance(.)=>xdmp:to-json()))/node()
};

declare function inst:instance-get-attachments(
    $document as document-node()
) as item()*
{
    if (exists($document//*:attachments/*))
    then $document//*:attachments/*
    else $document//*:attachments/text()
};


[ignore] Old admin webapp: jquery is no longer available in webapp.

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

xquery version "1.0";

import module namespace request="http://exist-db.org/xquery/request";
import module namespace xdb = "http://exist-db.org/xquery/xmldb";

if (starts-with($exist:path, "/backups")) then
    let $user := xdb:get-current-user()
    return
        if (not(xdb:is-admin-user($user))) then
            <dispatch xmlns="http://exist.sourceforge.net/NS/exist">
            	<redirect url="../admin.xql"/>
            </dispatch>
        else
            <dispatch xmlns="http://exist.sourceforge.net/NS/exist">
                <forward url="/admin/retrieve-backup.xql">
                    <add-parameter name="archive" value="{$exist:resource}"/>
                </forward>
            </dispatch>
else if ($exist:path eq "/") then
    <dispatch xmlns="http://exist.sourceforge.net/NS/exist">
        <redirect url="admin.xql"/>
    </dispatch>
else if ($exist:resource eq "admin.xql") then
    let $panel := request:get-parameter("panel", ())
    return
        <dispatch xmlns="http://exist.sourceforge.net/NS/exist">
        {
            if ($panel eq 'install') then
                <view>
                    <forward servlet="XSLTServlet">
                        (: Apply xsltforms.xsl stylesheet :)
                		<set-attribute name="xslt.stylesheet"
                			value="{$exist:root}/xforms/xsltforms/xsltforms.xsl"/>
                	    <set-attribute name="xslt.output.omit-xml-declaration" value="yes"/>
                	    <set-attribute name="xslt.output.indent" value="no"/>
                	    <set-attribute name="xslt.output.media-type" value="text/html"/>
                	    <set-attribute name="xslt.output.method" value="xhtml"/>
                	    <set-attribute name="xslt.baseuri" value="../xforms/xsltforms/"/>
                	</forward>
            	</view>
            else
                ()
        }
        </dispatch>

else if (contains($exist:path, "$shared/")) then
    <dispatch xmlns="http://exist.sourceforge.net/NS/exist">
        <forward url="/rest/apps/shared-resources/{substring-after($exist:path, '/$shared/')}"/>
    </dispatch>

else
    <ignore xmlns="http://exist.sourceforge.net/NS/exist">
        <cache-control cache="yes"/>
    </ignore>

xquery version "1.0";

import module namespace request="http://exist-db.org/xquery/request";
import module namespace xdb = "http://exist-db.org/xquery/xmldb";

if (starts-with($exist:path, "/backups")) then
    let $user := xdb:get-current-user()
    return
        if (not(xdb:is-admin-user($user))) then
            <dispatch xmlns="http://exist.sourceforge.net/NS/exist">
            	<redirect url="../admin.xql"/>
            </dispatch>
        else
            <dispatch xmlns="http://exist.sourceforge.net/NS/exist">
                <forward url="/admin/retrieve-backup.xql">
                    <add-parameter name="archive" value="{$exist:resource}"/>
                </forward>
            </dispatch>
else if ($exist:path eq "/") then
    <dispatch xmlns="http://exist.sourceforge.net/NS/exist">
        <redirect url="admin.xql"/>
    </dispatch>
else if ($exist:resource eq "admin.xql") then
    let $panel := request:get-parameter("panel", ())
    return
        <dispatch xmlns="http://exist.sourceforge.net/NS/exist">
        {
            if ($panel eq 'install') then
                <view>
                    <forward servlet="XSLTServlet">
                        (: Apply xsltforms.xsl stylesheet :)
                		<set-attribute name="xslt.stylesheet"
                			value="{$exist:root}/xforms/xsltforms/xsltforms.xsl"/>
                	    <set-attribute name="xslt.output.omit-xml-declaration" value="yes"/>
                	    <set-attribute name="xslt.output.indent" value="no"/>
                	    <set-attribute name="xslt.output.media-type" value="text/html"/>
                	    <set-attribute name="xslt.output.method" value="xhtml"/>
                	    <set-attribute name="xslt.baseuri" value="../xforms/xsltforms/"/>
                	</forward>
            	</view>
            else
                ()
        }
        </dispatch>
(: paths starting with /libs/ will be loaded from the webapp directory on the file system :)
else if (starts-with($exist:path, "/libs/")) then
    <dispatch xmlns="http://exist.sourceforge.net/NS/exist">
        <forward url="/{substring-after($exist:path, 'libs/')}" absolute="yes"/>
    </dispatch>

else
    <ignore xmlns="http://exist.sourceforge.net/NS/exist">
        <cache-control cache="yes"/>
    </ignore>


Continued fix for http://sourceforge.net/tracker/index.php?func=detail&aid=1535514&group_id=17691&atid=317691 ([ 1535514 ] decoding resources names in webadmin interface)
Creating collections with special chars......

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

(:
    Module: display and browse collections.
:)
module namespace browse="http://exist-db.org/xquery/admin-interface/browse";

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

import module namespace date="http://exist-db.org/xquery/admin-interface/date" at "dates.xqm";

(:
    Main function: outputs the page.
:)
declare function browse:main($user as xs:string, $passwd as xs:string) as element() {
    let $colName := request:get-parameter("collection", "/db"),
        $collection := xdb:collection($colName, $user, $passwd)
    return

            { browse:process-action($collection) }

Browsing Collection: {xdb:decode-uri(xs:anyURI($colName))}

            <form method="POST" enctype="multipart/form-data">
                {
                    browse:display-collection($collection)
                }

<input type="submit" name="action" value="Remove Selected"/>

                        <input type="submit" name="action" value="Create Collection"/>
                        New collection:

                        <input type="text" name="create" size="40"/>

                        <input type="submit" name="action" value="Upload"/>
                        <input type="file" size="30" name="upload"/>
                        Store as:

                        <input type="text" name="name" size="20"/>

                <input type="hidden" name="collection" value="{$colName}"/>
                <input type="hidden" name="panel" value="browse"/>
            </form>

};

(:
    Process an action.
:)
declare function browse:process-action($collection as object) as element()* {
    let $action := request:get-parameter("action", ())
    return
        util:catch("java.lang.Exception",
            if($action eq "Remove Selected") then
                browse:remove()
            else if($action eq "Create Collection") then
                browse:create-collection($collection)
            else if($action eq "Store") then
                browse:store($collection)
            else if($action eq "Upload") then
                browse:upload($collection)
            else
                (),

                An error occurred while processing the action:

                {$util:exception-message}

        )
};

(:
    Store uploaded content.
:)
declare function browse:upload($collection as object) as element() {
    let $name := request:get-parameter("name", ()),
        $docName :=
            if($name) then $name
            else request:get-uploaded-file-name("upload"),
        $file := request:get-uploaded-file("upload")
    return

Actions:

Storing uploaded content to: {$docName}

                {
                    xdb:decode-uri(xs:anyURI(xdb:store($collection, xdb:encode-uri($docName), $file)))
                }

};

(:
    Store files from an URI.

    Allowing this opens a security whole as a user can
    upload arbitrary files on the server if the process is running
    as root.
:)
declare function browse:store($collection as object) as element() {
    let $uri := request:get-parameter("uri", ()),
        $path := if(starts-with($uri, "file:")) then $uri
            else concat("file:", $uri),
        $docName := request:get-parameter("name", ())
    return

Actions:

Storing resources from URI: {$path}

                {
                    xdb:store($collection, $docName, xs:anyURI($path))
                }

};

(:
    Remove a set of resources.
:)
declare function browse:remove() as element() {
    let $resources := request:get-parameter("resource", ())
    return

Actions:

                {
                    for $resource in $resources
                    return
                        browse:remove-resource($resource)
                }

};

(:
    Remove a resource.
:)
declare function browse:remove-resource($resource as xs:string) as element()* {
    let $isBinary := util:binary-doc-available($resource)
    let $doc := if ($isBinary) then $resource else doc($resource)
    return
        if($doc) then (

Removing document: {xdb:decode-uri(xs:anyURI($resource))} ...
,
            xdb:remove(util:collection-name($doc), util:document-name($doc))
        ) else (

Removing collection: {xdb:decode-uri(xs:anyURI($resource))} ...
,
            xdb:remove($resource)
        )
};

(:
    Create a collection.
:)
declare function browse:create-collection($parent as object) as element() {
    let $newcol := request:get-parameter("create", ())
    return

Actions:

            {
                if($newcol) then
                    let $col := xdb:create-collection($parent, xdb:encode-uri($newcol))
                    return

Created collection: {xdb:decode-uri(xs:anyURI(util:collection-name($col)))}.

                else

No name specified for new collection!

            }

};

(:
    Display the contents of a collection in a table view.
:)
declare function browse:display-collection($collection as object)
as element() {
    let $colName := util:collection-name($collection)
    return

                Name
                Permissions
                Owner
                Group
                Created
                Modified
                Size (KB)

                Up

            {
                browse:display-child-collections($collection),
                browse:display-child-resources($collection)
            }

};

declare function browse:display-child-collections($collection as object)
as element()* {
    let $parent := util:collection-name($collection)
    for $child in xdb:get-child-collections($collection)
    let $path := concat($parent, '/', $child),
        $created := xdb:created($path)
    order by $child
    return

            <input type="checkbox" name="resource" value="{$path}"/>
            {xdb:decode-uri(xs:anyURI($child))}
            {xdb:permissions-to-string(xdb:get-permissions($path))}
            {xdb:get-owner($path)}
            {xdb:get-group($path)}
            {date:format-dateTime($created)}

};

declare function browse:display-child-resources($collection as object)
as element()* {
    let $parent := util:collection-name($collection)
    for $child in xdb:get-child-resources($collection)
    order by $child
    return

            <input type="checkbox" name="resource" value="{$parent}/{$child}"/>
            {xdb:decode-uri(xs:anyURI($child))}
            {xdb:permissions-to-string(xdb:get-permissions($collection, $child))}
            {xdb:get-owner($collection, $child)}
            {xdb:get-group($collection, $child)}
            {date:format-dateTime(xdb:created($collection, $child))}
            {date:format-dateTime(xdb:last-modified($collection, $child))}
            {fn:ceiling(xdb:size($collection, $child) div 1024)}

};

(:
    Get the name of the parent collection from a specified collection path.
:)
declare function browse:get-parent-collection($path as xs:string) as xs:string {
    if($path eq "/db") then
        $path
    else
        replace($path, "/[^/]*$", "")
};

(:
    Module: display and browse collections.
:)
module namespace browse="http://exist-db.org/xquery/admin-interface/browse";

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

import module namespace date="http://exist-db.org/xquery/admin-interface/date" at "dates.xqm";

(:
    Main function: outputs the page.
:)
declare function browse:main($user as xs:string, $passwd as xs:string) as element() {
    let $colName := request:get-parameter("collection", "/db"),
        $collection := xdb:collection($colName, $user, $passwd)
    return

            { browse:process-action($collection) }

Browsing Collection: {xdb:decode-uri(xs:anyURI($colName))}

            <form method="POST" enctype="multipart/form-data">
                {
                    browse:display-collection($collection)
                }

<input type="submit" name="action" value="Remove Selected"/>

                        <input type="submit" name="action" value="Create Collection"/>
                        New collection:

                        <input type="text" name="create" size="40"/>

                        <input type="submit" name="action" value="Upload"/>
                        <input type="file" size="30" name="upload"/>
                        Store as:

                        <input type="text" name="name" size="20"/>

                <input type="hidden" name="collection" value="{$colName}"/>
                <input type="hidden" name="panel" value="browse"/>
            </form>

};

(:
    Process an action.
:)
declare function browse:process-action($collection as object) as element()* {
    let $action := request:get-parameter("action", ())
    return
        util:catch("java.lang.Exception",
            if($action eq "Remove Selected") then
                browse:remove()
            else if($action eq "Create Collection") then
                browse:create-collection($collection)
            else if($action eq "Store") then
                browse:store($collection)
            else if($action eq "Upload") then
                browse:upload($collection)
            else
                (),

                An error occurred while processing the action:

                {$util:exception-message}

        )
};

(:
    Store uploaded content.
:)
declare function browse:upload($collection as object) as element() {
    let $name := request:get-parameter("name", ()),
        $docName :=
            if($name) then $name
            else request:get-uploaded-file-name("upload"),
        $file := request:get-uploaded-file("upload")
    return

Actions:

Storing uploaded content to: {$docName}

                {
                    xdb:decode-uri(xs:anyURI(xdb:store($collection, xdb:encode-uri($docName), $file)))
                }

};

(:
    Store files from an URI.

    Allowing this opens a security whole as a user can
    upload arbitrary files on the server if the process is running
    as root.
:)
declare function browse:store($collection as object) as element() {
    let $uri := request:get-parameter("uri", ()),
        $path := if(starts-with($uri, "file:")) then $uri
            else concat("file:", $uri),
        $docName := request:get-parameter("name", ())
    return

Actions:

Storing resources from URI: {$path}

                {
                    xdb:store($collection, $docName, xs:anyURI($path))
                }

};

(:
    Remove a set of resources.
:)
declare function browse:remove() as element() {
    let $resources := request:get-parameter("resource", ())
    return

Actions:

                {
                    for $resource in $resources
                    return
                        browse:remove-resource($resource)
                }

};

(:
    Remove a resource.
:)
declare function browse:remove-resource($resource as xs:string) as element()* {
    let $isBinary := util:binary-doc-available($resource)
    let $doc := if ($isBinary) then $resource else doc($resource)
    return
        if($doc) then (

Removing document: {xdb:decode-uri(xs:anyURI($resource))} ...
,
            xdb:remove(util:collection-name($doc), util:document-name($doc))
        ) else (

Removing collection: {xdb:decode-uri(xs:anyURI($resource))} ...
,
            xdb:remove($resource)
        )
};

(:
    Create a collection.
:)
declare function browse:create-collection($parent as object) as element() {
    let $newcol := request:get-parameter("create", ())
    return

Actions:

            {
                if($newcol) then
                    let $col := xdb:create-collection($parent, $newcol)
                    return

Created collection: {util:collection-name($col)}.

                else

No name specified for new collection!

            }

};

(:
    Display the contents of a collection in a table view.
:)
declare function browse:display-collection($collection as object)
as element() {
    let $colName := util:collection-name($collection)
    return

                Name
                Permissions
                Owner
                Group
                Created
                Modified
                Size (KB)

                Up

            {
                browse:display-child-collections($collection),
                browse:display-child-resources($collection)
            }

};

declare function browse:display-child-collections($collection as object)
as element()* {
    let $parent := util:collection-name($collection)
    for $child in xdb:get-child-collections($collection)
    let $path := concat($parent, '/', $child),
        $created := xdb:created($path)
    order by $child
    return

            <input type="checkbox" name="resource" value="{$path}"/>
            {xdb:decode-uri(xs:anyURI($child))}
            {xdb:permissions-to-string(xdb:get-permissions($path))}
            {xdb:get-owner($path)}
            {xdb:get-group($path)}
            {date:format-dateTime($created)}

};

declare function browse:display-child-resources($collection as object)
as element()* {
    let $parent := util:collection-name($collection)
    for $child in xdb:get-child-resources($collection)
    order by $child
    return

            <input type="checkbox" name="resource" value="{$parent}/{$child}"/>
            {xdb:decode-uri(xs:anyURI($child))}
            {xdb:permissions-to-string(xdb:get-permissions($collection, $child))}
            {xdb:get-owner($collection, $child)}
            {xdb:get-group($collection, $child)}
            {date:format-dateTime(xdb:created($collection, $child))}
            {date:format-dateTime(xdb:last-modified($collection, $child))}
            {fn:ceiling(xdb:size($collection, $child) div 1024)}

};

(:
    Get the name of the parent collection from a specified collection path.
:)
declare function browse:get-parent-collection($path as xs:string) as xs:string {
    if($path eq "/db") then
        $path
    else
        replace($path, "/[^/]*$", "")
};


minor modification  - was missing comma

minor modification  - was missing comma

xquery version "1.0";

(: ~
 : Module Name:             Value domain webpage
 :
 : Module Version           3.0
 :
 : Date                     27th September 2006
 :
 : Copyright                The cagrid consortium
 :
 : Module overview          Renders both types of value domain for viewing by the user of the metadata repository
 :
 :)

(:~
 :
 :    @author Steve Harris
 :    @version 1.0
~ :)

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
  value-meaning="http://www.cagrid.org/xquery/library/value-meaning"
  at "../library/m-value-meaning.xquery";

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

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

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

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

declare namespace openMDR = "http://www.cagrid.org/schema/openMDR";
declare namespace ISO11179= "http://www.cagrid.org/schema/ISO11179";
declare namespace request="http://exist-db.org/xquery/request";
declare namespace session="http://exist-db.org/xquery/session";
(: Using eXist-predefined namespace: xmldb :)

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 $compound_id := request:get-parameter("compound_id", "")

let $administered_item := lib-util:mdrElement("value_domain", $compound_id)

return
    if (request:get-parameter("as-xml",()))
    then (lib-rendering:as-xml($administered_item))
   else(
      let $administered_item_name := administered-item:preferred-name($administered_item)
      let $representation_class_id := xs:string($administered_item//openMDR:typed_by)
      let $conceptual_domain_id := $administered_item//openMDR:representing/text()

      let $title:=concat('Value Domain: ',$administered_item_name)

      let $content :=
  {
            lib-rendering:render_administered_item($administered_item),
            if (lower-case(local-name($administered_item))='non_enumerated_value_domain') then
            ()
            else
            (

Permissible values

                    {

permissible value

value meaning

begin date

                            {
                               for $permissible_value  in permissible-value:contained_in($administered_item)
                                  let $value := value:used_in($permissible_value)
                                  order by $value
                                  return

                                             {if ($value) then value:item($value) else ()}

                                             {
                                               string-join(
                                                   for $meaning in value-meaning:value-meaning($permissible_value/openMDR:contained_in/text())/openMDR:value_meaning_description
                                                   order by $meaning ascending
                                                   return $meaning, (: The sorting gives deterministic behaviour that is good for regression testing :)
                                                   " | "
                                               )
                                             }

                                           {permissible-value:begin_date($permissible_value)}

                      }

                    }

            ),

         lib-rendering:render_value_domain_common_properties($administered_item),
         lib-rendering:related-administered-items($administered_item),
         lib-rendering:value_domain_used_in_data_elements($compound_id)
      }

      return
         lib-rendering:txfrm-webpage($title, $content, true(),  true(), $compound_id, 'value_domain.xquery','../edit/editValueDomain.xquery')
   )

xquery version "1.0";

(: ~
 : Module Name:             Value domain webpage
 :
 : Module Version           3.0
 :
 : Date                     27th September 2006
 :
 : Copyright                The cagrid consortium
 :
 : Module overview          Renders both types of value domain for viewing by the user of the metadata repository
 :
 :)

(:~
 :
 :    @author Steve Harris
 :    @version 1.0
~ :)

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
  value-meaning="http://www.cagrid.org/xquery/library/value-meaning"
  at "../library/m-value-meaning.xquery";

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

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

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

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

declare namespace openMDR = "http://www.cagrid.org/schema/openMDR";
declare namespace ISO11179= "http://www.cagrid.org/schema/ISO11179";
declare namespace request="http://exist-db.org/xquery/request";
declare namespace session="http://exist-db.org/xquery/session";
(: Using eXist-predefined namespace: xmldb :)

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 $compound_id := request:get-parameter("compound_id", "")

let $administered_item := lib-util:mdrElement("value_domain", $compound_id)

return
    if (request:get-parameter("as-xml",()))
    then (lib-rendering:as-xml($administered_item))
   else(
      let $administered_item_name := administered-item:preferred-name($administered_item)
      let $representation_class_id := xs:string($administered_item//openMDR:typed_by)
      let $conceptual_domain_id := $administered_item//openMDR:representing/text()

      let $title:=concat('Value Domain: ',$administered_item_name)

      let $content :=
  {
            lib-rendering:render_administered_item($administered_item),
            if (lower-case(local-name($administered_item))='non_enumerated_value_domain') then
            ()
            else
            (

Permissible values

                    {

permissible value

value meaning

begin date

                            {
                               for $permissible_value  in permissible-value:contained_in($administered_item)
                                  let $value := value:used_in($permissible_value)
                                  order by	$value
                                  return

                                             {if ($value) then value:item($value) else ()}

                                             {
                                               string-join(
                                                   for $meaning in value-meaning:value-meaning($permissible_value/openMDR:contained_in/text())/openMDR:value_meaning_description
                                                   order by $meaning ascending
                                                   return $meaning, (: The sorting gives deterministic behaviour that is good for regression testing :)
                                                   " | "
                                               )
                                             }

                                           {permissible-value:begin_date($permissible_value)}

                      }

                    }

            ),

         lib-rendering:render_value_domain_common_properties($administered_item),
         lib-rendering:related-administered-items($administered_item),
         lib-rendering:value_domain_used_in_data_elements($compound_id)
      }

      return
         lib-rendering:txfrm-webpage($title, $content, true(),  true(), $compound_id, 'value_domain.xquery''../edit/editValueDomain.xquery')
   )