(: : 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-LGPL true {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
{ if ($collection) then ( ,

Package collection: {$collection}

) else () }
Application Properties
    { if (empty($repoConf)) then
  1. The template to use for generating the basic package structure.
  2. else () }
  3. Type of the package: a library contains XQuery modules and resources, but no web interface.
  4. By default some XQuery libraries and other resources will be imported from the "shared-resources" package. The generated package will thus have a dependancy on "shared-resources". Check this to avoid the dependency.
  5. A relative path to the collection where the package will be installed below the repository root. Leave this empty if the package does not need to be deployed into the database.

  6. The name of the package. This must be a URI.
  7. A short name for the app. This will be the name of the collection into which the app is installed.
  8. A descriptive title for the application.
  9. { let $status := $repoConf/repo:status/string() return }

  10. Optional: name of an XQuery script which will be run before the application is installed. Use this to create users, index configurations and the like.
  11. Optional: name of an XQuery script which will be run after the application was installed.
Description
  1. The author(s) of the application.
      { if (empty($repoConf)) then
    • else for $author in $repoConf/repo:author return
    • }
  2. A longer description of the application.
  3. Link to the author's website.
Default Permissions

Default permissions applied to all resources uploaded into the database. To set non-default permissions on particular resources, use a post-install script. If a user and password is specified, it will be created upon install if it does not yet exist.

{ let $owner := $repoConf/repo:permissions/@user let $password := $repoConf/repo:permissions/@password let $group := $repoConf/repo:permissions/@group let $mode := $repoConf/repo:permissions/@mode return
  1. The default permissions to be applied to resources. For collections and XQuery scripts, the execute flag ("x") will be set by the installer.
}
}; 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),

Failed to install application.

} catch * { response:set-status-code(500), Unknown error: {$err:description} }