(:
: eXide - web-based XQuery IDE
:
: Copyright (C) 2011 Wolfgang Meier
:
: This program is free software: you can redistribute it and/or modify
: it under the terms of the GNU General Public License as published by
: the Free Software Foundation, either version 3 of the License, or
: (at your option) any later version.
:
: This program is distributed in the hope that it will be useful,
: but WITHOUT ANY WARRANTY; without even the implied warranty of
: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
: GNU General Public License for more details.
:
: You should have received a copy of the GNU General Public License
: along with this program. If not, see .
:)
xquery version "3.0";
import module namespace tmpl="http://exist-db.org/xquery/template" at "tmpl.xql";
(:~
Edit the expath and repo app descriptors.
Functions to read, update the descriptors and deploy an app.
:)
declare namespace deploy="http://exist-db.org/eXide/deploy";
declare namespace expath="http://expath.org/ns/pkg";
declare namespace repo="http://exist-db.org/xquery/repo";
declare variable $app-root := request:get-attribute("app-root");
declare variable $deploy:ANT_FILE :=
;
declare function deploy:select-option($value as xs:string, $current as xs:string?, $label as xs:string) {
};
declare function deploy:get-app-root($collection as xs:string) {
if (not(starts-with($collection, "/"))) then
()
else if (doc(concat($collection, "/expath-pkg.xml"))) then
$collection
else if ($collection ne "/db") then
let $parent := replace($collection, "^(.*)/[^/]+$", "$1")
return
deploy:get-app-root($parent)
else
()
};
declare function deploy:store-expath($collection as xs:string?, $userData as xs:string*, $permissions as xs:int?) {
let $includeAll := request:get-parameter("includeall", ())
let $descriptor :=
{request:get-parameter("title", ())}
{
if (empty($includeAll)) then
else
()
}
return (
xmldb:store($collection, "expath-pkg.xml", $descriptor, "text/xml"),
xmldb:set-resource-permissions($collection, "expath-pkg.xml", $userData[1], $userData[2], $permissions)
)
};
declare function deploy:repo-descriptor() {
{
let $desc := request:get-parameter("description", ())
return
if ($desc) then $desc else request:get-parameter("title", ())
}
{
for $author in request:get-parameter("author", ())
return
{$author}
}
{request:get-parameter("website", ())}{request:get-parameter("status", ())}GNU-LGPLtrue{request:get-parameter("type", "application")}
{
let $target := request:get-parameter("target", ())
return
if (exists($target)) then
{$target}
else
()
}
{request:get-parameter("prepare", ())}{request:get-parameter("finish", ())}
{
if (request:get-parameter("owner", ())) then
let $group := request:get-parameter("group", ())
return
else
()
}
};
declare function deploy:store-repo($descriptor as element(), $collection as xs:string?, $userData as xs:string*, $permissions as xs:int?) {
(
xmldb:store($collection, "repo.xml", $descriptor, "text/xml"),
xmldb:set-resource-permissions($collection, "repo.xml", $userData[1], $userData[2], $permissions)
)
};
declare function deploy:mkcol-recursive($collection, $components, $userData as xs:string*, $permissions as xs:int?) {
if (exists($components)) then
let $permissions :=
if ($permissions) then
xmldb:string-to-permissions(deploy:set-execute-bit($permissions))
else
()
let $newColl := concat($collection, "/", $components[1])
return (
xmldb:create-collection($collection, $components[1]),
if (exists($userData)) then
xmldb:set-collection-permissions($newColl, $userData[1], $userData[2], $permissions)
else
(),
deploy:mkcol-recursive($newColl, subsequence($components, 2), $userData, $permissions)
)
else
()
};
declare function deploy:mkcol($path, $userData as xs:string*, $permissions as xs:int?) {
let $path := if (starts-with($path, "/db/")) then substring-after($path, "/db/") else $path
return
deploy:mkcol-recursive("/db", tokenize($path, "/"), $userData, $permissions)
};
declare function deploy:create-collection($collection as xs:string, $userData as xs:string+, $permissions as xs:int) {
let $target := collection($collection)
return
if ($target) then
$target
else
deploy:mkcol($collection, $userData, $permissions)
};
declare function deploy:check-group($group as xs:string) {
if (xmldb:group-exists($group)) then
()
else
xmldb:create-group($group)
};
declare function deploy:check-user($repoConf as element()) as xs:string+ {
let $perms := $repoConf/repo:permissions
let $user := if ($perms/@user) then $perms/@user/string() else xmldb:get-current-user()
let $group := if ($perms/@group) then $perms/@group/string() else xmldb:get-user-groups($user)[1]
let $create :=
if (xmldb:exists-user($user)) then
if (index-of(xmldb:get-user-groups($user), $group)) then
()
else (
deploy:check-group($group),
xmldb:add-user-to-group($user, $group)
)
else (
deploy:check-group($group),
xmldb:create-user($user, $perms/@password, $group, ())
)
return
($user, $group)
};
declare function deploy:target-permissions($repoConf as element()) as xs:int {
let $permissions := $repoConf/repo:permissions/@mode/string()
return
if ($permissions) then
if ($permissions castable as xs:int) then
util:base-to-integer(xs:int($permissions), 8)
else
xmldb:string-to-permissions($permissions)
else
util:base-to-integer(0775, 8)
};
declare function deploy:set-execute-bit($permissions as xs:int) {
let $mode := xmldb:permissions-to-string($permissions)
return
replace($mode, "(..).(..).(..).", "$1x$2x$3x")
};
declare function deploy:copy-templates($target as xs:string, $source as xs:string, $userData as xs:string+, $permissions as xs:int) {
let $null := deploy:mkcol($target, $userData, $permissions)
return
if (exists(collection($source))) then (
for $resource in xmldb:get-child-resources($source)
let $targetPath := xs:anyURI(concat($target, "/", $resource))
return (
xmldb:copy($source, $target, $resource),
let $mime := xmldb:get-mime-type($targetPath)
let $perms :=
if ($mime eq "application/xquery") then
xmldb:string-to-permissions(deploy:set-execute-bit($permissions))
else $permissions
return
xmldb:set-resource-permissions($target, $resource, $userData[1], $userData[2], $perms)
),
for $childColl in xmldb:get-child-collections($source)
return
deploy:copy-templates(concat($target, "/", $childColl), concat($source, "/", $childColl), $userData, $permissions)
) else
()
};
declare function deploy:store-templates-from-db($target as xs:string, $base as xs:string, $userData as xs:string+, $permissions as xs:int) {
let $template := request:get-parameter("template", "basic")
let $templateColl := concat($base, "/templates/", $template)
return
deploy:copy-templates($target, $templateColl, $userData, $permissions)
};
declare function deploy:chmod($collection as xs:string, $userData as xs:string+, $permissions as xs:int) {
(
xmldb:set-collection-permissions($collection, $userData[1], $userData[2], $permissions),
for $resource in xmldb:get-child-resources($collection)
let $path := concat($collection, "/", $resource)
let $mime := xmldb:get-mime-type($path)
let $perms :=
if ($mime eq "application/xquery") then
xmldb:string-to-permissions(deploy:set-execute-bit($permissions))
else $permissions
return
xmldb:set-resource-permissions($collection, $resource, $userData[1], $userData[2], $perms),
for $child in xmldb:get-child-collections($collection)
return
deploy:chmod(concat($collection, "/", $child), $userData, $permissions)
)
};
declare function deploy:store-ant($target as xs:string, $permissions as xs:int) {
let $abbrev := request:get-parameter("abbrev", "")
let $version := request:get-parameter("version", "1.0")
let $parameters :=
let $antXML := tmpl:expand-template($deploy:ANT_FILE, $parameters)
return
xmldb:store($target, "build.xml", $antXML)
};
declare function deploy:expand($collection as xs:string, $resource as xs:string, $parameters as element(parameters)) {
if (util:binary-doc-available($collection || "/" || $resource)) then
let $code :=
let $doc := util:binary-doc($collection || "/" || $resource)
return
util:binary-to-string($doc)
let $expanded := tmpl:parse($code, $parameters)
return
xmldb:store($collection, $resource, $expanded)
else
()
};
declare function deploy:expand-xql($target as xs:string) {
let $name := request:get-parameter("name", ())
let $includeTmpl := request:get-parameter("includeall", ())
let $template :=
if ($includeTmpl) then
"at "templates.xql""
else
""
let $parameters :=
let $cleanup :=
if (empty($includeTmpl) and util:binary-doc-available($target || "/modules/templates.xql")) then
xmldb:remove($target || "/modules", "templates.xql")
else
()
for $module in ("view.xql", "app.xql")
return
deploy:expand($target || "/modules", $module, $parameters)
};
declare function deploy:store-templates-from-fs($target as xs:string, $base as xs:string, $userData as xs:string+, $permissions as xs:int) {
let $pathSep := util:system-property("file.separator")
let $template := request:get-parameter("template", "basic")
let $templatesDir := concat($base, $pathSep, "templates", $pathSep, $template)
return (
xmldb:store-files-from-pattern($target, $templatesDir, "**/*", (), true(), "**/.svn/**"),
deploy:chmod($target, $userData, $permissions)
)
};
declare function deploy:store-templates($target as xs:string, $userData as xs:string+, $permissions as xs:int) {
let $base := substring-before(system:get-module-load-path(), "/modules")
return
if (starts-with($base, "xmldb:exist://")) then
deploy:store-templates-from-db($target, $base, $userData, $permissions)
else
deploy:store-templates-from-fs($target, $base, $userData, $permissions)
};
declare function deploy:store($collection as xs:string?, $expathConf as element()?) {
let $collection :=
if (starts-with($collection, "/")) then
$collection
else
repo:get-root() || $collection
let $repoConf := deploy:repo-descriptor()
let $permissions := deploy:target-permissions($repoConf)
let $userData := deploy:check-user($repoConf)
return
if (not($collection)) then
error(QName("http://exist-db.org/xquery/sandbox", "missing-collection"), "collection parameter missing")
else
let $create := deploy:create-collection($collection, $userData, $permissions)
let $null := (
deploy:store-expath($collection, $userData, $permissions),
deploy:store-repo($repoConf, $collection, $userData, $permissions),
if (empty($expathConf)) then (
deploy:store-templates($collection, $userData, $permissions),
deploy:store-ant($collection, $permissions),
deploy:expand-xql($collection)
) else
()
)
return
$collection
};
declare function deploy:create-app($collection as xs:string?, $expathConf as element()?) {
let $collection := deploy:store($collection, $expathConf)
return
if (empty($expathConf)) then
let $expathConf := doc($collection || "/expath-pkg.xml")/*
return (
util:declare-option("exist:serialize", "method=json media-type=application/json"),
deploy:deploy($collection, $expathConf),
{ $collection }
)
else
{ $collection }
};
declare function deploy:view($collection as xs:string?, $expathConf as element()?, $repoConf as element()?) {
let $null := util:declare-option("exist:serialize", "method=html media-type=text/html")
return
};
declare function deploy:package($collection as xs:string, $expathConf as element()) {
let $name := concat($expathConf/@abbrev, "-", $expathConf/@version, ".xar")
let $xar := compression:zip(xs:anyURI($collection), true(), $collection)
let $mkcol := deploy:mkcol("/db/system/repo", (), ())
return
xmldb:store("/db/system/repo", $name, $xar, "application/zip")
};
declare function deploy:download($collection as xs:string, $expathConf as element()) {
let $name := concat($expathConf/@abbrev, "-", $expathConf/@version, ".xar")
let $xar := compression:zip(xs:anyURI($collection), true(), $collection)
return (
response:set-header("Content-Disposition", concat("attachment; filename=", $name)),
response:stream-binary($xar, "application/zip", $name)
)
};
declare function deploy:deploy($collection as xs:string, $expathConf as element()) {
let $pkg := deploy:package($collection, $expathConf)
let $null := (
repo:remove($expathConf/@name),
repo:install-from-db($pkg)
)
return
()
};
declare function deploy:get-info-from-descriptor($collection as xs:string) {
let $expathConf := doc(concat($collection, "/expath-pkg.xml"))/expath:package
let $repoConf := doc(concat($collection, "/repo.xml"))/repo:meta
let $user := xmldb:get-current-user()
let $auth := if ($user) then xmldb:is-admin-user($user) else false()
return
{$repoConf/repo:target/string()}{$repoConf/repo:deployed/string()}{$auth}{ request:get-attribute("$exist:prefix") || "/" || substring-after($collection, repo:get-root()) }
};
declare function deploy:get-info($collection as xs:string) {
let $null := util:declare-option("exist:serialize", "method=json media-type=application/json")
let $root := deploy:get-app-root($collection)
return
if ($root) then
deploy:get-info-from-descriptor($root)
else
};
let $target := request:get-parameter("target", ())
let $collectionParam := request:get-parameter("collection", ())
let $collection :=
if ($collectionParam) then
let $root := deploy:get-app-root($collectionParam)
return
if ($root) then $root else $collectionParam
else
$target
let $info := request:get-parameter("info", ())
let $deploy := request:get-parameter("deploy", ())
let $download := request:get-parameter("download", ())
let $expathConf := if ($collection) then xmldb:xcollection($collection)/expath:package else ()
let $repoConf := if ($collection) then xmldb:xcollection($collection)/repo:meta else ()
let $abbrev := request:get-parameter("abbrev", ())
return
try {
if ($download) then
deploy:download($collection, $expathConf)
else if ($info) then
deploy:get-info($info)
else if ($abbrev) then
deploy:create-app($collection, $expathConf)
else
deploy:view($collection, $expathConf, $repoConf)
} catch exerr:EXXQDY0003 {
response:set-status-code(403),
You don't have permissions to access or write the application archive.
Please correct the location or log in as a different user.
} catch exerr:EXREPOINSTALL001 {
response:set-status-code(404),