From e0ca9acd6a29bfe1c255b0a7dd904332086e7cd5 Mon Sep 17 00:00:00 2001 From: Charafeddine Cheraa Date: Tue, 31 May 2022 13:15:36 +0300 Subject: [PATCH] added option to choose navigation root --- modules/lib/api.json | 8 +++ modules/lib/api/document.xql | 3 +- modules/lib/epub.xql | 2 +- package-lock.json | 16 ++++++ package.json | 1 + test/navigation.test.js | 94 ++++++++++++++++++++++++++++++++++++ 6 files changed, 122 insertions(+), 2 deletions(-) create mode 100644 test/navigation.test.js diff --git a/modules/lib/api.json b/modules/lib/api.json index 64d98d8f6..2ed21e000 100644 --- a/modules/lib/api.json +++ b/modules/lib/api.json @@ -884,6 +884,14 @@ "type": "string", "example": "docbook.odd" } + }, + { + "name": "nav-root", + "in": "query", + "schema": { + "type": "string", + "example": "/1/4" + } } ], "responses": { diff --git a/modules/lib/api/document.xql b/modules/lib/api/document.xql index 326475404..ee182448b 100644 --- a/modules/lib/api/document.xql +++ b/modules/lib/api/document.xql @@ -326,7 +326,8 @@ declare function dapi:epub($request as map(*)) { }; declare %private function dapi:work2epub($request as map(*), $id as xs:string, $work as document-node(), $lang as xs:string?) { - let $config := $config:epub-config($work, $lang) + let $navRoot := fn:fold-left(tokenize($request?parameters?nav-root, '/'), (), function($a, $b) { (if (not(empty($a))) then $a else $work)/node()[position()=xs:integer($b)] }) + let $config := map:merge(($config:epub-config($work, $lang), map { 'navRoot': $navRoot })) let $odd := head(($request?parameters?odd, $config:default-odd)) let $oddName := replace($odd, "^([^/\.]+).*$", "$1") let $cssDefault := util:binary-to-string(util:binary-doc($config:output-root || "/" || $oddName || ".css")) diff --git a/modules/lib/epub.xql b/modules/lib/epub.xql index 6b027a321..b1b49e8f3 100644 --- a/modules/lib/epub.xql +++ b/modules/lib/epub.xql @@ -257,7 +257,7 @@ declare function epub:nav-entry($config, $text) { diff --git a/package-lock.json b/package-lock.json index 1edfcbe6d..e766a2363 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "@teipublisher/pb-components": "latest" }, "devDependencies": { + "adm-zip": "^0.5.9", "axios": "^0.21.1", "chai": "^4.2.0", "chai-openapi-response-validator": "^0.9.4", @@ -832,6 +833,15 @@ "node": ">=0.4.0" } }, + "node_modules/adm-zip": { + "version": "0.5.9", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.9.tgz", + "integrity": "sha512-s+3fXLkeeLjZ2kLjCBwQufpI5fuN+kIGBxu6530nVQZGVol0d7Y/M88/xw9HGGUcJjKf8LutN3VPRUBq6N7Ajg==", + "dev": true, + "engines": { + "node": ">=6.0" + } + }, "node_modules/ajv": { "version": "6.12.4", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.4.tgz", @@ -4594,6 +4604,12 @@ "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", "dev": true }, + "adm-zip": { + "version": "0.5.9", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.9.tgz", + "integrity": "sha512-s+3fXLkeeLjZ2kLjCBwQufpI5fuN+kIGBxu6530nVQZGVol0d7Y/M88/xw9HGGUcJjKf8LutN3VPRUBq6N7Ajg==", + "dev": true + }, "ajv": { "version": "6.12.4", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.4.tgz", diff --git a/package.json b/package.json index cf68c752f..c12079080 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "@teipublisher/pb-components": "latest" }, "devDependencies": { + "adm-zip": "^0.5.9", "axios": "^0.21.1", "chai": "^4.2.0", "chai-openapi-response-validator": "^0.9.4", diff --git a/test/navigation.test.js b/test/navigation.test.js new file mode 100644 index 000000000..e90d565ab --- /dev/null +++ b/test/navigation.test.js @@ -0,0 +1,94 @@ +const util = require('./util.js'); +const path = require('path'); +const FormData = require('form-data'); +const chai = require('chai'); +const chaiXML = require('chai-xml'); +const expect = chai.expect; +const chaiResponseValidator = require('chai-openapi-response-validator'); +const jsdom = require("jsdom"); +const zip = require('adm-zip'); +const { JSDOM } = jsdom; + +const spec = path.resolve("./modules/lib/api.json"); +chai.use(chaiResponseValidator(spec)); +chai.use(chaiXML); + +const testXml = ` + + + + EPUB Navigation Test + + +

+ + +

+ + + + + +

+ Pre-Document 1 +

Pre-Document 1 body

+
+
+ Pre-Document 2 +

Pre-Document 2 body

+
+
+ Pre-Document 3 +

Pre-Document 3 body

+
+ + +
+ Document 4 +

Document 4 body

+
+
+ Document 5 +

Document 5 body

+
+
+ Document 6 +

Document 6 body

+
+ + +
`; + +async function getNav(data) { + return new zip(Buffer.from(data)).getEntries().find(({ entryName }) => entryName === 'OEBPS/nav.xhtml')?.getData().toString('utf-8'); +} + +describe('/api/document/{document}}/epub?nav-root=', function() { + before(async () => { + await util.login(); + const formData = new FormData() + formData.append('files[]', testXml, "nav.xml"); + const res = await util.axios.post('upload/playground', formData, { + headers: formData.getHeaders() + }); + expect(res.data).to.have.length(1); + expect(res.data[0].name).to.equal('/db/apps/tei-publisher/data/playground/nav.xml'); + expect(res).to.satisfyApiSpec; + }); + + it('let tei-publisher determine the navigation root', async () => { + const res = await util.axios.get('document/playground%2Fnav.xml/epub', { responseType: 'arraybuffer' }); + expect(res.status).to.equal(200); + const document = new JSDOM(await getNav(res.data), { contentType: "application/xml" }).window.document; + expect(document.querySelectorAll('li').length).to.equal(3); + }); + + it('define navigation root', async () => { + const res = await util.axios.get('document/playground%2Fnav.xml/epub?nav-root=1/4', { responseType: 'arraybuffer' }); + expect(res.status).to.equal(200); + const document = new JSDOM(await getNav(res.data), { contentType: "application/xml" }).window.document; + expect(document.querySelectorAll('li').length).to.equal(6); + }); + + after(util.logout); +}); \ No newline at end of file