Skip to content
Draft

26 q1 #619

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
85c5194
Use $global for $wdb:data/resources
dariok Jan 3, 2026
537f9ff
improve creation of JSON out of REST lists
dariok Jan 4, 2026
4257ec8
REST2 list project views
dariok Jan 4, 2026
4c1df4b
use <list> uniformly for list-like results
dariok Jan 4, 2026
cd27003
improve definition of a list of views
dariok Jan 4, 2026
bb47a92
restructure and improve API defintion
dariok Jan 5, 2026
2882985
REST: use name+s when creating JSON for list-like XML
dariok Jan 5, 2026
86965fa
update npm packages
dariok Jan 5, 2026
a23ae86
[REST2] more refinements on YAML
dariok Jan 6, 2026
87c87da
[REST2] GET projects/{ed}
dariok Jan 6, 2026
a2a1d0c
fix a missing variable declaration
dariok Jan 6, 2026
5b3db79
[REST2] DELETE projects/{ed}
dariok Jan 6, 2026
323bffe
[REST2] GET /projects/{ed}/subprojects
dariok Jan 7, 2026
5d33c96
[REST2] 409 when collection in use
dariok Jan 7, 2026
d6d4fb4
model: case $ed = 'data'
dariok Jan 7, 2026
6942078
update cannot delete root element
dariok Jan 7, 2026
1e086b1
update project index: include project title
dariok Jan 8, 2026
af3acc3
[REST2] /projects/{ed}/views/{view}
dariok Jan 8, 2026
d45e0c6
generic function to apply framework XSLTs
dariok Jan 8, 2026
2b9813e
[REST2] projects/{ed}/views/navigation
dariok Jan 8, 2026
4c25674
start: use new REST functions for content and nav
dariok Jan 8, 2026
122c2d0
functions processing return maps
dariok Jan 9, 2026
a2b46c9
upload form uses `edition` as default destination
dariok Jan 9, 2026
b88d71b
add comments in templated HTML
dariok Jan 9, 2026
99998d6
Addins with new model
dariok Jan 19, 2026
3310976
implement list of project resources
dariok Jan 19, 2026
75bf2d5
move nav.xsl to respect new structure in resources
dariok Feb 2, 2026
2c64283
add documentation for applySpecificXsl()
dariok Feb 2, 2026
806da0a
generic TEI transformation: p may have `@style`
dariok Feb 2, 2026
aaa2ff1
styling of fn-number: is now a button that needs more info
dariok Feb 2, 2026
d53491b
set paths for REST v1 and v2 in config.xml
dariok Feb 3, 2026
cd2c2a9
additional security headers
dariok Feb 3, 2026
7bd200b
get content must not return map as content
dariok Feb 4, 2026
a4f3322
update jQuery and jQueryUI
dariok Feb 4, 2026
687f754
move apiv2.html (Swagger UI) to rest2
dariok Feb 4, 2026
9a825ad
set headers when viewing standard HTML files
dariok Feb 4, 2026
01fd25d
move search.xsl to resources/xsl and use function for specific XSLTs
dariok Feb 6, 2026
e4fb2bd
improve security headers
dariok Feb 9, 2026
fe02917
applySpecificXsl extended to allow parameters
dariok Feb 9, 2026
26c10fe
improve HTML putput of error reports
dariok Feb 10, 2026
4ab4971
return better info when an XSLT was not found
dariok Feb 10, 2026
50c1f8e
controller: auth is handled by roaster
dariok Feb 10, 2026
96f2dac
move function to parse multipart to a separate module
dariok Feb 11, 2026
fda74de
parse multipart bodies for API requests other than PUT
dariok Feb 11, 2026
c44e120
absolte positioning for div in aside
dariok Feb 12, 2026
bc50aca
update npm packages for mocha testing and linting
dariok Feb 12, 2026
83e2cb8
improvements for footnotes
dariok Feb 12, 2026
5eea166
footnotes: use variables to keep track of types
dariok Feb 12, 2026
d0c61bf
common.xsl: create a strucutred footer with links
dariok Feb 12, 2026
9ec4002
fn-numbers in em, not in rem
dariok Feb 13, 2026
d0dfc9f
PUT and POST to create or update files
dariok Feb 20, 2026
4c7fd2a
use wdbFile:getFullPath instead of directly accessing project index
dariok Feb 21, 2026
d23d689
add @data-type to buttons from rs
dariok Mar 2, 2026
5370763
file upload in projects endpoint
dariok Mar 2, 2026
a006a08
REST2 projects: do not allow deleting data collection
dariok Mar 2, 2026
6664b3c
PUT/POST resources in a project, including tests
dariok Mar 4, 2026
e4ec5ff
[REST2] DELETE project: delete wdbmeta entries
dariok Mar 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 25 additions & 16 deletions edoc/admin/admin.xqm
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ module namespace wdbAdmin = "https://github.com/dariok/wdbplus/Admin";

import module namespace config = "https://github.com/dariok/wdbplus/config" at "../modules/wdb-config.xqm";
import module namespace wdbErr = "https://github.com/dariok/wdbplus/errors" at "../modules/error.xqm";
import module namespace wdbFiles = "https://github.com/dariok/wdbplus/files" at "../modules/wdb-files.xqm";
import module namespace wdbm = "https://github.com/dariok/wdbplus/model" at "../modules/model.xqm";

declare namespace meta = "https://github.com/dariok/wdbplus/wdbmeta";
Expand All @@ -24,31 +23,40 @@ function wdbAdmin:start ( $node as node(), $model as map(*), $ed as xs:string )
wdbm:populateModel((), $ed, "", "", "")
};

declare function wdbAdmin:getEd ( $node as node(), $model as map(*) ) as element(meta) {
<meta name="ed" content="{ $model?ed }" />
};
declare function wdbAdmin:getEd ( $node as node(), $model as map(*) ) as item()+ {(
comment { "Created in admin.xqm for "|| $node/@data-template },
<meta name="ed" content="{ $model?ed }" />,
<meta name="path" content="{ $model?pathToEd }" />
)};

declare function wdbAdmin:heading ($node as node(), $model as map(*)) {
declare function wdbAdmin:heading ( $node as node(), $model as map(*) ) as element()+ {
let $opts := if (request:get-parameter('job', '') != '')
then <span class="dispOpts"><a href="global.html">globale Optionen</a></span>
else ()

return (

<h1>{
if ($model?page = 'admin.html')
then "Admin-Seite"
else if ($model?page = 'global.html')
then "Globale Einstellungen"
else if ($model?ed = '')
then "Projekte"
else ("Projekt ", <i>{$model?title}</i>, " (" || $model?ed || ")")
comment { "Created in admin.xqm for "|| $node/@data-template },
if ($model?page = 'admin.html') then
"Admin-Seite"
else if ($model?page = 'global.html') then
"Globale Einstellungen"
else if ($model?ed = '') then
"Projekte"
else (
"Projekt ",
<i>{$model?title}</i>,
" (" || $model?ed || ")"
)
}</h1>,
$opts
)
};

declare function wdbAdmin:getAside ($node as node(), $model as map(*)) as element() {
declare function wdbAdmin:getAside ( $node as node(), $model as map(*) ) as element() {
<aside>
comment { "Created in admin.xqm for "|| $node/@data-template }
<h3>Funktionen</h3>
{
switch ($model?page)
Expand All @@ -68,11 +76,12 @@ declare function wdbAdmin:getAside ($node as node(), $model as map(*)) as elemen
</aside>
};

declare function wdbAdmin:css ( $node as node(), $model as map(*) ) as element()* {
declare function wdbAdmin:css ( $node as node(), $model as map(*) ) as item()* {
comment { "Created in admin.xqm for "|| $node/@data-template },
if ( unparsed-text-available($config:data || "/resources/css/wdb.css") )
then <link rel="stylesheet" type="text/css" href="../data/resources/css/wdb.css" />
then <link rel="stylesheet" type="text/css" href="$global/css/wdb.css" />
else (),
if ( unparsed-text-available($config:data || "/resources/css/admin.css") )
then <link rel="stylesheet" type="text/css" href="../data/resources/css/admin.css" />
then <link rel="stylesheet" type="text/css" href="$global/css/admin.css" />
else ()
};
50 changes: 24 additions & 26 deletions edoc/admin/directoryForm.html
Original file line number Diff line number Diff line change
@@ -1,29 +1,27 @@
<div data-template="templates:surround" data-template-with="templates/admin.html" data-template-at="container">
<h1>Funktion</h1>
<form action="#" method="post">
<fieldset id="selectTask">
<input type="radio" name="task" id="do">Verzeichnis ohne Eintragen in wdbmeta</input><br/>
<input type="radio" name="task" id="dm">Verzeichnis mit Eintragen in wdbmeta</input><br/>
<input type="radio" name="task" id="fo">Datei(en) ohne Eintragen in wdbmeta</input><br/>
<input type="radio" name="task" id="fi" checked="checked">Datei(en) mit Eintragen in wdbmeta</input><br/>
<h1>Funktion</h1>
<form action="#" method="post">
<fieldset id="selectTask">
<input type="radio" name="task" id="dm">Verzeichnis(se) (relative Pfade)</input><br/>
<input type="radio" name="task" id="fi" checked="checked">Datei(en)</input>
</fieldset>
<fieldset id="selectTarget">
<label>Collection: </label>
<pre/>
<br/>
<label>Zielcollection auswählen: </label>
<select name="target">
<option/>
</select>
</fieldset>
<fieldset id="selectInputDir">
<label>Datei auswählen: </label>
<input type="file" id="picker" name="fileList" multiple="multiple" />
</fieldset>
<input type="submit" disabled="disabled"/>
</form>
<p>
<img src="../resources/FhHRx.gif" style="display: none;" alt="loading"/>
</p>
<table id="results"/>
<fieldset id="selectTarget">
<label>Collection: </label>
<pre/>
<br/>
<label>Zielcollection auswählen: </label>
<select name="target">
<option/>
</select>
</fieldset>
<fieldset id="selectInputDir">
<label>Datei auswählen: </label>
<input type="file" id="picker" name="fileList" multiple="multiple" />
</fieldset>
<input type="submit" disabled="disabled"/>
</form>
<p>
<img src="$shared/images/FhHRx.gif" style="display: none;" alt="loading"/>
</p>
<table id="results"/>
</div>
22 changes: 19 additions & 3 deletions edoc/config.xml
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,33 @@
<server>http://localhost:8080/exist/apps/edoc</server>

<!-- Full path for the instance’s REST base URL -->
<rest>http://localhost:8080/exist/restxq/edoc/</rest>
<rest version="1">http://localhost:8080/exist/restxq/edoc/</rest>
<rest version="2">https://localhost/exist/apps/edoc/api/v2/</rest>

<!-- the collection that will hold all the projects. Adjust if you decide to put your projects elsewhere
default: /db/apps/edoc/data -->
<data>/db/apps/edoc/data</data>

<!-- standard HTTP reponse headers for HTML files -->
<headers>
<header name="Content-Security-Policy" value="
base-uri 'self';
connect-src 'self' https://example.org;
default-src 'self';
form-action 'self';
frame-ancestors 'none';
frame-src 'self';
img-src 'self' data: https://example.org;
object-src 'none';
script-src 'self' https://example.org 'sha256-i5FN1OSK7gDNa3opBmzMU9YJGHPyoa6XHjjunO2922g=';
style-src 'self' 'unsafe-inline' https://example.org;
upgrade-insecure-requests;" />
<header name="Permissions-Policy" value="geolocation=(self), fullscreen=(self)"/>
<header name="Referrer-Policy" value="no-referrer-when-downgrade"/>
<header name="Referrer-Policy" value="strict-origin-when-cross-origin"/>
<header name="X-Content-Type-Options" value="nosniff"/>
<header name="X-Frame-Options" value="DENY"/>
<header name="X-XSS-Protection" value="1; mode=block"/>
<header name="Strict-Transport-Security" value="max-age=31536000" />
</headers>

<!-- Allowed origins for CORS requests – these may be necessary when wdb+ is behind a rewriting proxy
Expand All @@ -51,6 +65,8 @@

<!-- Sources for some Blobs -->
<externalSources>
<source name="jquery" path="$shared/jquery-3.6.3.min.js" />
<source name="jquery" path="$shared/js/jquery-4.0.0.min.js" />
<source name="jquery-ui-js" path="$shared/js/jquery-ui.min.js" />
<source name="jquery-ui-css" path="$shared/css/jquery-ui.min.css" />
</externalSources>
</config>
52 changes: 35 additions & 17 deletions edoc/controller.xql
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,11 @@
:)
xquery version "3.1";

import module namespace login = "http://exist-db.org/xquery/login" at "resource:org/exist/xquery/modules/persistentlogin/login.xql";
import module namespace request = "http://exist-db.org/xquery/request" at "java:org.exist.xquery.functions.request.RequestModule";
(: import module namespace sm = "http://exist-db.org/xquery/securitymanager" at "java:org.exist.xquery.functions.securitymanager.SecurityManagerModule";:)
import module namespace wdba = "https://github.com/dariok/wdbplus/auth" at "modules/auth.xqm";
import module namespace login = "http://exist-db.org/xquery/login" at "resource:org/exist/xquery/modules/persistentlogin/login.xql";
import module namespace request = "http://exist-db.org/xquery/request" at "java:org.exist.xquery.functions.request.RequestModule";

declare namespace exist = "http://exist.sourceforge.net/NS/exist";
declare namespace config = "https://github.com/dariok/wdbplus/config";
declare namespace exist = "http://exist.sourceforge.net/NS/exist";

declare variable $exist:path external;
declare variable $exist:resource external;
Expand All @@ -19,16 +18,17 @@ declare variable $exist:prefix external;
(: declare variable $exist:root external; :)

declare variable $local:isget := request:get-method() = ("GET","get");
declare variable $local:config := doc("/db/apps/edoc/config.xml")/config:config;

util:log("info", "request:get-method(): " || request:get-method()),
util:log("info", "exist:path: " || $exist:path),
util:log("info", request:get-method() || " " || request:get-url() || ' ? ' || request:get-query-string() || " -> resource: " || $exist:resource),

(: static HTML page for API documentation should be served directly to make sure it is always accessible :)
if (
( $local:isget and $exist:path eq "/apiv2.html" ) or
( $local:isget and matches($exist:path, "^/[^/]+\.json$", "s") )
( $local:isget and $exist:resource = ('v2.json', 'apiv2.html') )
) then
<dispatch xmlns="http://exist.sourceforge.net/NS/exist" />
<dispatch xmlns="http://exist.sourceforge.net/NS/exist">
<forward url="{$exist:controller}/rest2/{$exist:resource}"/>
</dispatch>
(: login :)
else if ( $exist:resource = 'login' ) then
<dispatch xmlns="http://exist.sourceforge.net/NS/exist">
Expand Down Expand Up @@ -69,15 +69,33 @@ else if ( ends-with($exist:resource, ".html") ) then
<dispatch xmlns="http://exist.sourceforge.net/NS/exist">
<view>
<forward url="{$exist:controller}/modules/view.xql">
</forward>
{
for $header in $local:config//config:header
return <set-header>{ $header/@* }</set-header>
}
</forward>
</view>
</dispatch>
else if ( contains($exist:path, "/$shared/") ) then
<dispatch xmlns="http://exist.sourceforge.net/NS/exist">
<forward url="{$exist:controller}/resources/{substring-after($exist:path, '/$shared/')}">
<set-header name="Cache-Control" value="max-age=604800, must-revalidate"/>
</forward>
</dispatch>
(: generic resources :)
else if ( contains($exist:path, "/$shared/") ) then
<dispatch xmlns="http://exist.sourceforge.net/NS/exist">
<forward url="{$exist:controller}/resources/{substring-after($exist:path, '/$shared/')}">
{
for $header in $local:config//config:header
return <set-header>{ $header/@* }</set-header>
}
</forward>
</dispatch>
(: instance specific resources :)
else if ( contains($exist:path, "/$global/") ) then
<dispatch xmlns="http://exist.sourceforge.net/NS/exist">
<forward url="{$exist:controller}/data/resources/{substring-after($exist:path, '/$global/')}">
{
for $header in $local:config//config:header
return <set-header>{ $header/@* }</set-header>
}
</forward>
</dispatch>
else if ( ends-with($exist:path, ".xql") ) then
<dispatch xmlns="http://exist.sourceforge.net/NS/exist">
<set-header name="Cache-Control" value="no-cache"/>
Expand Down
14 changes: 8 additions & 6 deletions edoc/index/index-projects.xq
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,22 @@ xquery version "3.1";
declare namespace index = "https://github.com/dariok/wdbplus/index";
declare namespace meta = "https://github.com/dariok/wdbplus/wdbmeta";

update replace doc("/db/apps/edoc/index/project-index.xml")/index:index with <index xmlns="https://github.com/dariok/wdbplus/index"/>,
update replace doc("/db/apps/edoc/index/file-index.xml")/index:index with <index xmlns="https://github.com/dariok/wdbplus/index"/>,
for $project in //meta:projectMD
update delete doc("/db/apps/edoc/index/project-index.xml")/index:index/*,
update delete doc("/db/apps/edoc/index/file-index.xml")/index:index/*,
for $project in collection('/db/apps/edoc/data')//meta:projectMD
let $path := util:collection-name($project)
where contains($path, '/data/')

let $files := $project//meta:file
let $file-entries := for $file in $files
return <file xmlns="https://github.com/dariok/wdbplus/index" xml:id="{ $file/@xml:id }" project="{ base-uri($file) }" />
return <file xmlns="https://github.com/dariok/wdbplus/index"
xml:id="{ $file/@xml:id }"
project="{ base-uri($file) }"
/>

return (
update insert <project xmlns="https://github.com/dariok/wdbplus/index"
xml:id="{ $project/@xml:id }"
path="{ $path }"
title="{ $project//meta:title[@type='main'] }"
/> into doc("/db/apps/edoc/index/project-index.xml")/index:index,
if ( not(empty($file-entries)) )
then update insert $file-entries into doc("/db/apps/edoc/index/file-index.xml")/index:index
Expand Down
2 changes: 1 addition & 1 deletion edoc/modules/addin.xqm
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ declare namespace wdbadd = "https://github.com/dariok/wdbplus/addins";
(: load the main XQuery module for the requested addin. It is mandatory these implement wdbadd:main($map as map(*)) :)
declare function wdbAddinMain:body ( $node as node(), $model as map(*) ) as element()+ {
let $addinName := substring-before(substring-after(request:get-uri(), 'addins/'), '/')
, $path := substring-before(base-uri(), '/addin.xqm') || "/addins/" || $addinName || "/addin.xqm"
, $path := "/db/apps/edoc/addins/" || $addinName || "/addin.xqm"
, $map := map { "location-hints": $path }

let $module := try {
Expand Down
73 changes: 42 additions & 31 deletions edoc/modules/app.xqm
Original file line number Diff line number Diff line change
Expand Up @@ -291,41 +291,52 @@ declare function wdb:getXslFromWdbMeta ( $infoFileLoc as xs:string, $id as xs:st
"no process found for target '" || $target || "' and view '" || $view || "' in " || $infoFileLoc
)
};
(: END LOCAL HELPER FUNCTIONS :)

(: HELPERS FOR REST AND HTTP REQUESTS :)
declare function wdb:parseMultipart ( $data, $header ) {
let $boundary := $header => substring-after('boundary=') => translate('"', '')
return map:merge(
for $m in tokenize($data, "--" || $boundary) return
if (string-length($m) lt 6)
then ()
(:~
: Apply a project specific XSLT to some XML
:
: @param $xml The XML to be transformed
: @param $edPath The path to the project
: @param $name The name of the XSLT file to be applied
:
: @returns The transformed XML
:
: The lookup order is:
: 1) project resources
: 2) instance resources
: 3) global resources
:)
declare function wdb:applySpecificXsl ( $xml as node(), $edPath as xs:string, $name as xs:string ) as node() {
wdb:applySpecificXsl($xml, $edPath, $name, ())
};
(:~
: Apply a project specific XSLT to some XML
:
: @param $xml The XML to be transformed
: @param $edPath The path to the project
: @param $name The name of the XSLT file to be applied
: @param $parameters (optional) parameters to be passed to the XSLT
:
: @returns The transformed XML
:
: The lookup order is:
: 1) project resources
: 2) instance resources
: 3) global resources
:)
declare function wdb:applySpecificXsl ( $xml as node(), $edPath as xs:string, $name as xs:string, $parameters as element(parameters)? ) as node() {
let $xsl := if ( doc-available($edPath || "/resources/xsl/" || $name) ) then
doc($edPath || "/resources/xsl/" || $name)
else if ( doc-available("/db/apps/edoc/data/resources/xsl/" || $name) ) then
doc("/db/apps/edoc/data/resources/xsl/" || $name)
else
let $parts := (tokenize($m, "(^\s*$){2}", "m"))[normalize-space() != ""]
let $header := map:merge(
for $line in tokenize($parts[1], "\n") return
if (normalize-space($line) eq "")
then ()
else
let $val := $line => substring-after(': ') => normalize-space()
let $value := if (contains($val, '; '))
then map:merge(
for $entry in tokenize($val, '; ') return
if (contains($entry, '='))
then map:entry ( substring-before($entry, '='), translate(substring-after($entry, '='), '"', '') )
else map:entry ( "text", $entry )
)
else $val
return map:entry(substring-before($line, ': '), $value)
)

(: empty lines in the body will also cause splitting; hence, recombine everything except the header :)
return map:entry(($header?Content-Disposition?name, 'name')[1],
map { "header" : $header, "body" : string-join($parts[position() > 1], '\n') }
)
)
doc("/db/apps/edoc/resources/xsl/" || $name)

return transform:transform($xml, $xsl, $parameters)
};
(: END LOCAL HELPER FUNCTIONS :)

(: HELPERS FOR REST AND HTTP REQUESTS :)
(:~
: Get a MIME type from an extension and an optional XML namespace
:
Expand Down
Loading