diff --git a/docs/index.rst b/docs/index.rst index 3f40e6337..a190afa97 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -12,3 +12,4 @@ Welcome to libkiwix's documentation! usage api/ref_api + widget diff --git a/docs/widget.rst b/docs/widget.rst new file mode 100644 index 000000000..40104b37c --- /dev/null +++ b/docs/widget.rst @@ -0,0 +1,82 @@ +Kiwix serve widget +==================== + +Introduction +------------ + +The kiwix-serve widget provides an easy to embed way to show the `kiwix-serve` homepage. + +Usage +----- + +To use the widget, simply add an iframe with its `src` attribute set to the `widget` endpoint. +Example HTML Page :: + + + + + Widget Test + + + + + + +This creates an iframe with the kiwix-serve homepage contents. + +Arguments are explained below. + +Possible Arguments +------------------- + +Currently, the following arguments are supported. + +disabledesc (value = N/A) + Disables the description part of a tile. + +disablefilter (value = N/A) + Disables the search filters: language, category, tag and search function. + +disableclick (value = N/A) + Disables clicking the book to open it for reading. + +disabledownload (value = N/A) + Disables the download button (if avaialable at all) on the tile. + + +Custom CSS and JS +----------------- + +You can add your custom CSS rules and Javascript code to the widget. + +To do that, use the following code as template:: + + + + + + +The CSS/JS fields are optional, you may send both or only one. + diff --git a/src/server/internalServer.cpp b/src/server/internalServer.cpp index 43411260f..7127658e9 100644 --- a/src/server/internalServer.cpp +++ b/src/server/internalServer.cpp @@ -577,6 +577,9 @@ std::unique_ptr InternalServer::handle_request(const RequestContext& r if (isEndpointUrl(url, "catch")) return handle_catch(request); + if (isEndpointUrl(url, "widget")) + return handle_widget(request); + std::string contentUrl = m_root + "/content" + url; const std::string query = request.get_query(); if ( ! query.empty() ) @@ -866,6 +869,11 @@ std::unique_ptr InternalServer::handle_random(const RequestContext& re } } +std::unique_ptr InternalServer::handle_widget(const RequestContext& request) +{ + return ContentResponse::build(*this, RESOURCE::templates::widget_html, get_default_data(), "text/html; charset=utf-8", true); +} + std::unique_ptr InternalServer::handle_captured_external(const RequestContext& request) { std::string source = ""; diff --git a/src/server/internalServer.h b/src/server/internalServer.h index 82574378c..f49b58189 100644 --- a/src/server/internalServer.h +++ b/src/server/internalServer.h @@ -143,6 +143,7 @@ class InternalServer { std::unique_ptr handle_content(const RequestContext& request); std::unique_ptr handle_raw(const RequestContext& request); std::unique_ptr handle_locally_customized_resource(const RequestContext& request); + std::unique_ptr handle_widget(const RequestContext& request); std::vector search_catalog(const RequestContext& request, kiwix::OPDSDumper& opdsDumper); diff --git a/static/resources_list.txt b/static/resources_list.txt index 653fadd2f..79ec99a58 100644 --- a/static/resources_list.txt +++ b/static/resources_list.txt @@ -9,6 +9,7 @@ skin/iso6391To3.js skin/isotope.pkgd.min.js skin/index.js skin/autoComplete.min.js +skin/widget.js skin/taskbar.css skin/index.css skin/fonts/Poppins.ttf @@ -20,6 +21,7 @@ templates/search_result.xml templates/error.html templates/error.xml templates/index.html +templates/widget.html templates/suggestion.json templates/head_taskbar.html templates/taskbar_part.html diff --git a/static/skin/index.js b/static/skin/index.js index fd1c338ea..8847b076f 100644 --- a/static/skin/index.js +++ b/static/skin/index.js @@ -1,4 +1,4 @@ -(function() { +const kiwixServe = (function() { const root = document.querySelector(`link[type='root']`).getAttribute('href'); const incrementalLoadingParams = { start: 0, @@ -17,6 +17,7 @@ let params = new URLSearchParams(window.location.search || filters || ''); let timer; let languages = {}; + let allowBookClick = true; function queryUrlBuilder() { let url = `${root}/catalog/search?`; @@ -85,7 +86,7 @@ } function generateBookHtml(book, sort = false) { - const link = book.querySelector('link[type="text/html"]').getAttribute('href'); + let link = book.querySelector('link[type="text/html"]').getAttribute('href'); let iconUrl; book.querySelectorAll('link[rel="http://opds-spec.org/image/thumbnail"]').forEach(link => { if (link.getAttribute('type').split(';')[1] == 'width=48' && !iconUrl) { @@ -120,6 +121,9 @@ } const faviconAttr = iconUrl != undefined ? `style="background-image: url('${iconUrl}')"` : ''; const languageAttr = langCode != '' ? `title="${language}" aria-label="${language}"` : 'style="background-color: transparent"'; + if (!allowBookClick) { + link = "javascript:void(0)"; + } divTag.innerHTML = `
@@ -247,14 +251,16 @@ toggleFooter(); } const kiwixResultText = document.querySelector('.kiwixHomeBody__results') - if (results) { - let resultText = `${results} books`; - if (results === 1) { - resultText = `${results} book`; + if (kiwixResultText) { + if (results) { + let resultText = `${results} books`; + if (results === 1) { + resultText = `${results} book`; + } + kiwixResultText.innerHTML = resultText; + } else { + kiwixResultText.innerHTML = ``; } - kiwixResultText.innerHTML = resultText; - } else { - kiwixResultText.innerHTML = ``; } loader.style.display = 'none'; return books; @@ -265,16 +271,20 @@ await fetch(query).then(async (resp) => { const data = new window.DOMParser().parseFromString(await resp.text(), 'application/xml'); let optionStr = ''; - data.querySelectorAll('entry').forEach(entry => { - const title = getInnerHtml(entry, 'title'); - const value = getInnerHtml(entry, valueEntryNode); - const hfTitle = humanFriendlyTitle(title); - if (valueEntryNode == 'language') { - languages[value] = hfTitle; - } - optionStr += (hfTitle != '') ? `` : ''; - }); - document.querySelector(nodeQuery).innerHTML += optionStr; + const entryList = data.querySelectorAll('entry'); + const nodeQueryElem = document.querySelector(nodeQuery); + if (entryList && nodeQueryElem) { + entryList.forEach(entry => { + const title = getInnerHtml(entry, 'title'); + const value = getInnerHtml(entry, valueEntryNode); + const hfTitle = humanFriendlyTitle(title); + if (valueEntryNode == 'language') { + languages[value] = hfTitle; + } + optionStr += (hfTitle != '') ? `` : ''; + }); + nodeQueryElem.innerHTML += optionStr; + } }); } @@ -388,6 +398,10 @@ } }); } + + function disableBookClick() { + allowBookClick = false; + } function addTagElement(tagValue, resetFilter) { const tagElement = document.getElementsByClassName('tagFilterLabel')[0]; @@ -429,13 +443,15 @@ } } - window.addEventListener('resize', (event) => { + function updateBookCount(event) { if (timer) {clearTimeout(timer)} timer = setTimeout(() => { incrementalLoadingParams.count = incrementalLoadingParams.count && viewPortToCount(); loadSubset(); }, 100, event); - }); + } + + window.addEventListener('resize', (event) => updateBookCount(event)); window.addEventListener('scroll', loadSubset); @@ -479,6 +495,7 @@ } } updateVisibleParams(); + updateBookCount(); document.getElementById('kiwixSearchForm').onsubmit = (event) => {event.preventDefault()}; if (!window.location.search) { const browserLang = navigator.language.split('-')[0]; @@ -491,5 +508,10 @@ } setCookie(filterCookieName, params.toString()); } + + return { + updateBookCount, + disableBookClick + }; })(); diff --git a/static/skin/widget.js b/static/skin/widget.js new file mode 100644 index 000000000..a93b74c79 --- /dev/null +++ b/static/skin/widget.js @@ -0,0 +1,107 @@ +function disableSearchFilters(widgetStyles) { + const hideNavRule = ` + .kiwixNav { + display: none; + }`; + const hideResultsLabelRule = ` + .kiwixHomeBody__results { + display: none; + }`; + const hideTagFilterRule = ` + .book__tags { + pointer-events: none; + }`; + insertNewCssRules(widgetStyles, [hideNavRule, hideResultsLabelRule, hideTagFilterRule]); +} + +function disableBookClick() { + kiwixServe.disableBookClick(); +} + +function disableDownload(widgetStyles) { + const hideBookDownloadRule = ` + .book__download { + display: none; + }`; + insertNewCssRules(widgetStyles, [hideBookDownloadRule]); +} + +function disableDescription(widgetStyles) { + const decreaseHeightRule = ` + .book__wrapper { + height:128px; + grid-template-rows: 70px 0 1fr 1fr; + }`; + const hideDescRule = ` + .book__description { + display: none; + }`; + insertNewCssRules(widgetStyles, [decreaseHeightRule, hideDescRule]); +} + +function hideFooter(widgetStyles) { + const hideFooterRule = ` + .kiwixfooter { + display: none !important; + }`; + insertNewCssRules(widgetStyles, [hideFooterRule]); +} + +function insertNewCssRules(stylesheet, ruleList) { + if (stylesheet) { + for (rule of ruleList) { + stylesheet.insertRule(rule, 0); + } + } +} + +function addCustomCss(cssCode) { + let customCSS = document.createElement('style'); + customCSS.innerHTML = cssCode; + document.head.appendChild(customCSS); +} + +function addCustomJs(jsCode) { + new Function(`"use strict";${jsCode}`)(); +} + +function handleMessages(event) { + if ('css' in event.data) { + addCustomCss(event.data.css); + } + if ('js' in event.data) { + addCustomJs(event.data.js); + } +} + +function handleWidget() { + const params = new URLSearchParams(window.location.search || filters || ''); + const widgetStyleElem = document.createElement('style'); + document.head.appendChild(widgetStyleElem); + + const widgetStyles = widgetStyleElem.sheet; + + const disableFilters = params.has('disablefilter'); + const disableClick = params.has('disableclick'); + const disableDwld = params.has('disabledownload'); + const disableDesc = params.has('disabledesc'); + + const blankBase = document.createElement('base'); + blankBase.target = '_blank'; + document.head.appendChild(blankBase); // open all links in new tab + + if (disableFilters) + disableSearchFilters(widgetStyles); + if (disableClick) + disableBookClick(); + if (disableDwld) + disableDownload(widgetStyles); + if (disableDesc) + disableDescription(widgetStyles); + + hideFooter(widgetStyles); + kiwixServe.updateBookCount(); +} + +window.addEventListener('message', handleMessages); +handleWidget(); \ No newline at end of file diff --git a/static/templates/widget.html b/static/templates/widget.html new file mode 100644 index 000000000..545e4fa44 --- /dev/null +++ b/static/templates/widget.html @@ -0,0 +1,64 @@ + + + + + + Welcome to Kiwix Server + + + + + + + + +
+
+
+ +
+
+ +
+
+
+ + + +
+
+
+
+

+
+
+
+
+
+ + + diff --git a/test/server.cpp b/test/server.cpp index 886fcf542..3475c2051 100644 --- a/test/server.cpp +++ b/test/server.cpp @@ -184,7 +184,7 @@ R"EXPECTEDRESULT( href="/ROOT/skin/index.css?cacheid=56e818cd" src: url("/ROOT/skin/fonts/Roboto.ttf?cacheid=84d10248") format("truetype"); - + )EXPECTEDRESULT" }, {