diff --git a/modules/app.xqm b/modules/app.xqm index 9fc20f485..862ecadde 100644 --- a/modules/app.xqm +++ b/modules/app.xqm @@ -28,6 +28,7 @@ import module namespace gl="http://xquery.weber-gesamtausgabe.de/modules/gl" at import module namespace er="http://xquery.weber-gesamtausgabe.de/modules/external-requests" at "external-requests.xqm"; import module namespace dev-app="http://xquery.weber-gesamtausgabe.de/modules/dev/dev-app" at "dev/dev-app.xqm"; import module namespace functx="http://www.functx.com"; +import module namespace kwic="http://exist-db.org/xquery/kwic"; import module namespace templates="http://exist-db.org/xquery/templates"; import module namespace str="http://xquery.weber-gesamtausgabe.de/modules/str" at "xmldb:exist:///db/apps/WeGA-WebApp-lib/xquery/str.xqm"; import module namespace app-shared="http://xquery.weber-gesamtausgabe.de/modules/app-shared" at "xmldb:exist:///db/apps/WeGA-WebApp-lib/xquery/app-shared.xqm"; @@ -899,7 +900,8 @@ declare declare %templates:default("lang", "en") function app:print-wega-bio($node as node(), $model as map(*), $lang as xs:string) as element(div)* { - let $bio := wega-util:transform($model('doc')//(tei:note[@type='bioSummary'] | tei:event[tei:head] | tei:note[parent::tei:org]), doc(concat($config:xsl-collection-path, '/persons.xsl')), config:get-xsl-params(())) + let $query-result:= app:inject-query($model?doc/*) + let $bio := wega-util:transform($query-result//(tei:note[@type='bioSummary'] | tei:event[tei:head] | tei:note[parent::tei:org]), doc(concat($config:xsl-collection-path, '/persons.xsl')), config:get-xsl-params(())) return if(some $i in $bio satisfies $i instance of element()) then $bio else @@ -1251,11 +1253,11 @@ declare default return doc(concat($config:xsl-collection-path, '/var.xsl')) let $textRoot := switch($docType) - case 'diaries' return $doc/tei:ab - case 'works' return $doc/mei:mei - case 'var' case 'addenda' return $doc//tei:text/tei:body/(tei:div[@xml:lang=$lang] | tei:divGen | tei:div[not(@xml:lang)]) - case 'thematicCommentaries' return $doc//tei:text/(tei:body | tei:back) - default return $doc//tei:text/tei:body + case 'diaries' return $doc/tei:ab ! app:inject-query(.) + case 'works' return $doc/mei:mei ! app:inject-query(.) + case 'var' case 'addenda' return ($doc//tei:text/tei:body ! app:inject-query(.))/(tei:div[@xml:lang=$lang] | tei:divGen | tei:div[not(@xml:lang)]) + case 'thematicCommentaries' return $doc//tei:text/tei:body ! app:inject-query(.) | $doc//tei:text/tei:back + default return $doc//tei:text/tei:body ! app:inject-query(.) let $body := if(functx:all-whitespace({$textRoot})) then @@ -1289,6 +1291,27 @@ declare } }; +(:~ + : Search and highlight query strings in a document + : Helper function for app:prepare-text() + : + : @param $input must be node() that is indexed by Lucene + : @return if a hit was found in $input, an expanded copy of the $input with exist:match elements wrapping the hits. + : If no hit was found, the initial $input is returned + :) +declare %private function app:inject-query($input as node()) { + let $q := request:get-parameter('q', '') + return + if($q) then + let $sanitized-query-string := wega-util:strip-diacritics(str:normalize-space(str:sanitize(string-join($q, ' ')))) + let $query := search:create-lucene-query-element($sanitized-query-string) + let $result := ft:query($input, $query) + return + if($result) then $result ! kwic:expand(.) + else $input + else $input +}; + declare %templates:wrap function app:series($node as node(), $model as map(*)) as xs:string { @@ -1695,7 +1718,7 @@ declare return element {name($node)} { $node/@*[not(name(.) = 'href')], - if($node[self::xhtml:a]) then attribute href {app:createUrlForDoc($model('doc'), $lang)} + if($node[self::xhtml:a]) then attribute href {app:createUrlForDoc($model('doc'), $lang) || (if(map:contains($model, 'query-string-org')) then ('?q=' || $model?query-string-org) else ())} else (), if($title instance of xs:string or $title instance of text() or count($title) gt 1) then $title else $title/node() diff --git a/modules/search.xqm b/modules/search.xqm index becec6332..7ece00e77 100644 --- a/modules/search.xqm +++ b/modules/search.xqm @@ -17,6 +17,8 @@ import module namespace str="http://xquery.weber-gesamtausgabe.de/modules/str" a import module namespace wdt="http://xquery.weber-gesamtausgabe.de/modules/wdt" at "wdt.xqm"; import module namespace lang="http://xquery.weber-gesamtausgabe.de/modules/lang" at "lang.xqm"; import module namespace wega-util="http://xquery.weber-gesamtausgabe.de/modules/wega-util" at "wega-util.xqm"; +import module namespace controller="http://xquery.weber-gesamtausgabe.de/modules/controller" at "controller.xqm"; +import module namespace app="http://xquery.weber-gesamtausgabe.de/modules/app" at "app.xqm"; import module namespace functx="http://www.functx.com"; declare variable $search:ERROR := QName("http://xquery.weber-gesamtausgabe.de/modules/search", "Error"); @@ -46,7 +48,7 @@ declare return switch($docType) (: search page :) - case 'search' return search:search(map:merge(($model, $filters, map:entry('docID', 'indices')))) + case 'search' return search:search-session(map:merge(($model, $filters, map:entry('docID', 'indices'))), search:search#1) (: controller sends docType=persons which needs to be turned into "personsPlus" here :) case 'persons' return search:list(map:merge(($filters, map:put($model, 'docType', 'personsPlus')))) (: various list views :) @@ -160,7 +162,7 @@ declare : Search results and other goodies for the *search* page ~:) declare %private function search:search($model as map(*)) as map(*) { - let $updatedModel := search:prepare-search-string($model) + let $updatedModel := $model let $docTypes := if($updatedModel?query-docTypes = 'all') then ($search:wega-docTypes, 'var', 'addenda') (: silently add 'var' (= special pages, e.g. "Impressum/About" or "Sonderband/Special Volume") to the list of docTypes :) else $search:wega-docTypes[.=$updatedModel?query-docTypes] @@ -176,6 +178,10 @@ declare %private function search:search($model as map(*)) as map(*) { let $fulltext-search := if($updatedModel('query-string')) then search:merge-hits($docTypes ! search:fulltext($filtered-results, $updatedModel('query-string'), $updatedModel?filters, .)) else $filtered-results + let $store-session := + if(count($fulltext-search) gt 0) + then session:set-attribute('wegasearch', map:merge(($updatedModel, map:entry('search-results', $fulltext-search)))) + else () return map:merge(($updatedModel, map:entry('search-results', $fulltext-search))) }; @@ -235,7 +241,7 @@ declare %private function search:fulltext($items as item()*, $searchString as xs (:~ : Parse the query string and create an XML query element for the lucene search ~:) -declare %private function search:create-lucene-query-element($searchString as xs:string) as element(query) { +declare function search:create-lucene-query-element($searchString as xs:string) as element(query) { let $groups := analyze-string($searchString, '(-?"(.+?)")')/* (: split into fn:match – for expressions in parentheses – and fn:non-match elements :) let $queryElement := function($elementName as xs:string, $token as item()) as element() { element {$elementName} { @@ -432,3 +438,49 @@ declare %private function search:prepare-search-string($model as map()) as map(* } )) }; + +(:~ + : Cache search results in browser session + : + : @param $model the current model map of the templating module + : @param $callback a callback function to actually do the search if the session is empty + : @return a map object {'filters': {}, 'search-results': {}, 'query-string-org': '', 'query-docTypes': ('') } +~:) +declare %private function search:search-session($model as map(), $callback as function() as map(*)) as map(*) { + let $updatedModel := search:prepare-search-string($model) + let $session-exists := + try { + count(session:get-attribute('wegasearch')?search-results) gt 0 + and session:get-attribute('wegasearch')?query-string-org = $updatedModel?query-string-org + and functx:sequence-deep-equal(session:get-attribute('wegasearch')?filters?*, $updatedModel?filters?*) + and functx:sequence-deep-equal(session:get-attribute('wegasearch')?query-docTypes, $updatedModel?query-docTypes) + } + catch * {false()} + return + if($session-exists) + then session:get-attribute('wegasearch') + else $callback($updatedModel) +}; + +declare + %templates:wrap + function search:get-session-for-singleview($node as node(), $model as map(*)) as map()? { + let $wegasearch := session:get-attribute('wegasearch') + let $index-of-current-doc := functx:index-of-node($wegasearch?search-results?doc, $model?doc) + let $page := ceiling($index-of-current-doc div config:entries-per-page()) + let $url := controller:resolve-link('$link/search', $model) || '?q=' || $wegasearch?query-string-org || '&d=' || string-join($wegasearch?query-docTypes, '&d=') || '&page=' || $page + let $search-prev-item-url := + if($index-of-current-doc gt 1) then app:createUrlForDoc($wegasearch?search-results[$index-of-current-doc - 1]?doc, $model?lang) || '?q=' || $wegasearch?query-string-org + else '#' + let $search-next-item-url := + if($index-of-current-doc lt count($wegasearch?search-results)) then app:createUrlForDoc($wegasearch?search-results[$index-of-current-doc + 1]?doc, $model?lang) || '?q=' || $wegasearch?query-string-org + else '#' + return + map:merge(( + $wegasearch, + map:entry('index-of-current-doc', $index-of-current-doc), + map:entry('search-backlink', $url), + map:entry('search-prev-item-url', $search-prev-item-url), + map:entry('search-next-item-url', $search-next-item-url) + )) +}; diff --git a/resources/sass/pages/_search.scss b/resources/sass/pages/_search.scss index 46cb779c1..83dff0e76 100644 --- a/resources/sass/pages/_search.scss +++ b/resources/sass/pages/_search.scss @@ -35,7 +35,7 @@ } } -*[class*="hi-"], *[class*="hi-"] *, .kwic .hi { +*[class*="hi-"], *[class*="hi-"] *, .hi { color: #ffffff !important; background-color: $primary !important; } diff --git a/templates/document.html b/templates/document.html index d7f8fd638..37e96f225 100644 --- a/templates/document.html +++ b/templates/document.html @@ -11,28 +11,43 @@

CARL MARIA VON WEBER AN
-
-
- +
+
+
+ +
+
+ + back +
-
- - back +
+
diff --git a/templates/person.html b/templates/person.html index 0053ea53e..c514214df 100644 --- a/templates/person.html +++ b/templates/person.html @@ -10,16 +10,31 @@

Weber, Carl Maria von

-
-
- +
+
+
+ +
+
+ back +
-
- back +
+
diff --git a/templates/var.html b/templates/var.html index 79b139dd2..797f3e450 100644 --- a/templates/var.html +++ b/templates/var.html @@ -11,31 +11,46 @@

CARL MARIA VON WEBER AN

-
-
- - - +
+
+
+ + + +
+
+ + back +
-
- - back +
+
diff --git a/templates/work.html b/templates/work.html index c5bfbfe3a..e1854764a 100644 --- a/templates/work.html +++ b/templates/work.html @@ -10,17 +10,32 @@

Oberon

-
-
- +
+
+
+ +
+
+ back +
-
- back +
+
diff --git a/xsl/common_main.xsl b/xsl/common_main.xsl index 73ce6a59e..9b6a343b5 100644 --- a/xsl/common_main.xsl +++ b/xsl/common_main.xsl @@ -8,7 +8,9 @@ xmlns:functx="http://www.functx.com" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:teix="http://www.tei-c.org/ns/Examples" - xmlns:mei="http://www.music-encoding.org/ns/mei" version="2.0"> + xmlns:mei="http://www.music-encoding.org/ns/mei" + xmlns:exist="http://exist.sourceforge.net/NS/exist" + version="2.0"> @@ -825,4 +827,15 @@ + + + + + + + hi + + + +