diff --git a/include/meson.build b/include/meson.build index 149c31251..b522020cc 100644 --- a/include/meson.build +++ b/include/meson.build @@ -10,6 +10,7 @@ headers = [ 'reader.h', 'entry.h', 'searcher.h', + 'search_renderer.h', 'kiwixserve.h', 'name_mapper.h' ] diff --git a/include/search_renderer.h b/include/search_renderer.h new file mode 100644 index 000000000..375f6beba --- /dev/null +++ b/include/search_renderer.h @@ -0,0 +1,85 @@ +/* + * Copyright 2011 Emmanuel Engelhart + * + * 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 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifndef KIWIX_SEARCH_RENDERER_H +#define KIWIX_SEARCH_RENDERER_H + +#include + +namespace kiwix +{ + +class Searcher; +class NameMapper; +/** + * The SearcherRenderer class is used to render a search result to a html page. + */ +class SearchRenderer +{ + public: + /** + * The default constructor. + * + * @param humanReadableName The global zim's humanReadableName. + * Used to generate pagination links. + */ + SearchRenderer(Searcher* searcher, NameMapper* mapper); + + ~SearchRenderer(); + + void setSearchPattern(const std::string& pattern); + + /** + * Set the search content id. + */ + void setSearchContent(const std::string& name); + + /** + * Set protocol prefix. + */ + void setProtocolPrefix(const std::string& prefix); + + /** + * Set search protocol prefix. + */ + void setSearchProtocolPrefix(const std::string& prefix); + + /** + * Generate the html page with the resutls of the search. + */ + std::string getHtml(); + + protected: + std::string beautifyInteger(const unsigned int number); + Searcher* mp_searcher; + NameMapper* mp_nameMapper; + std::string searchContent; + std::string searchPattern; + std::string protocolPrefix; + std::string searchProtocolPrefix; + unsigned int resultCountPerPage; + unsigned int estimatedResultCount; + unsigned int resultStart; + unsigned int resultEnd; +}; + + +} + +#endif diff --git a/include/searcher.h b/include/searcher.h index f40ab9624..9c0058258 100644 --- a/include/searcher.h +++ b/include/searcher.h @@ -56,24 +56,14 @@ struct SearcherInternal; /** * The Searcher class is reponsible to do different kind of search using the * fulltext index. - * - * Searcher may (if compiled with ctpp2) be used to - * generate a html page for the search result. This use a template that need a - * humanReaderName. This feature is only used by kiwix-serve and this should be - * move outside of Searcher (and with a better API). If you don't use the html - * rendering (getHtml method), you better should simply ignore the different - * humanReadeableName attributes (or give an empty string). */ class Searcher { public: /** * The default constructor. - * - * @param humanReadableName The global zim's humanReadableName. - * Used to generate pagination links. */ - Searcher(const string& humanReadableName = ""); + Searcher(); ~Searcher(); @@ -81,11 +71,13 @@ class Searcher * Add a reader (containing embedded fulltext index) to the search. * * @param reader The Reader for the zim containing the fulltext index. - * @param humanReaderName The human readable name of the reader. * @return true if the reader has been added. * false if the reader cannot be added (no embedded fulltext index present) */ - bool add_reader(Reader* reader, const std::string& humanReaderName); + bool add_reader(Reader* reader); + + + Reader* get_reader(int index); /** * Start a search on the zim associated to the Searcher. @@ -151,22 +143,8 @@ class Searcher */ unsigned int getEstimatedResultCount(); - /** - * Set protocol prefix. - * Only used by getHtml. - */ - bool setProtocolPrefix(const std::string prefix); - - /** - * Set search protocol prefix. - * Only used by getHtml. - */ - bool setSearchProtocolPrefix(const std::string prefix); - - /** - * Generate the html page with the resutls of the search. - */ - string getHtml(); + unsigned int getResultStart() { return resultStart; } + unsigned int getResultEnd() { return resultEnd; } protected: std::string beautifyInteger(const unsigned int number); @@ -177,16 +155,11 @@ class Searcher const bool verbose = false); std::vector readers; - std::vector humanReaderNames; SearcherInternal* internal; std::string searchPattern; - std::string protocolPrefix; - std::string searchProtocolPrefix; - unsigned int resultCountPerPage; unsigned int estimatedResultCount; unsigned int resultStart; unsigned int resultEnd; - std::string contentHumanReadableId; private: void reset(); diff --git a/src/android/kiwixsearcher.cpp b/src/android/kiwixsearcher.cpp index 317c50e8a..1c46836d0 100644 --- a/src/android/kiwixsearcher.cpp +++ b/src/android/kiwixsearcher.cpp @@ -52,7 +52,7 @@ JNIEXPORT void JNICALL Java_org_kiwix_kiwixlib_JNIKiwixSearcher_addReader( { auto searcher = SEARCHER; - searcher->add_reader(*(Handle::getHandle(env, reader)), ""); + searcher->add_reader(*(Handle::getHandle(env, reader))); } JNIEXPORT void JNICALL Java_org_kiwix_kiwixlib_JNIKiwixSearcher_search( diff --git a/src/meson.build b/src/meson.build index 65635ca7f..9990f5698 100644 --- a/src/meson.build +++ b/src/meson.build @@ -9,6 +9,7 @@ kiwix_sources = [ 'reader.cpp', 'entry.cpp', 'searcher.cpp', + 'search_renderer.cpp', 'subprocess.cpp', 'aria2.cpp', 'tools/base64.cpp', diff --git a/src/search_renderer.cpp b/src/search_renderer.cpp new file mode 100644 index 000000000..769bb6a21 --- /dev/null +++ b/src/search_renderer.cpp @@ -0,0 +1,151 @@ +/* + * Copyright 2011 Emmanuel Engelhart + * + * 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 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + + +#include + +#include "search_renderer.h" +#include "searcher.h" +#include "reader.h" +#include "library.h" +#include "name_mapper.h" + +#include + +#include +#include "kiwixlib-resources.h" + + +namespace kiwix +{ + +/* Constructor */ +SearchRenderer::SearchRenderer(Searcher* searcher, NameMapper* mapper) + : mp_searcher(searcher), + mp_nameMapper(mapper), + protocolPrefix("zim://"), + searchProtocolPrefix("search://?") +{} + +/* Destructor */ +SearchRenderer::~SearchRenderer() = default; + +void SearchRenderer::setSearchPattern(const std::string& pattern) +{ + this->searchPattern = pattern; +} + +void SearchRenderer::setSearchContent(const std::string& name) +{ + this->searchContent = name; +} + +void SearchRenderer::setProtocolPrefix(const std::string& prefix) +{ + this->protocolPrefix = prefix; +} + +void SearchRenderer::setSearchProtocolPrefix(const std::string& prefix) +{ + this->searchProtocolPrefix = prefix; +} + +std::string SearchRenderer::getHtml() +{ + kainjow::mustache::data results{kainjow::mustache::data::type::list}; + + mp_searcher->restart_search(); + Result* p_result = NULL; + while ((p_result = mp_searcher->getNextResult())) { + kainjow::mustache::data result; + result.set("title", p_result->get_title()); + result.set("url", p_result->get_url()); + result.set("snippet", p_result->get_snippet()); + auto readerIndex = p_result->get_readerIndex(); + auto reader = mp_searcher->get_reader(readerIndex); + result.set("resultContentId", mp_nameMapper->getNameForId(reader->getId())); + + if (p_result->get_wordCount() >= 0) { + result.set("wordCount", kiwix::beautifyInteger(p_result->get_wordCount())); + } + + results.push_back(result); + delete p_result; + } + + // pages + kainjow::mustache::data pages{kainjow::mustache::data::type::list}; + + auto resultStart = mp_searcher->getResultStart(); + auto resultEnd = mp_searcher->getResultEnd(); + auto resultCountPerPage = resultEnd - resultStart; + auto estimatedResultCount = mp_searcher->getEstimatedResultCount(); + + unsigned int pageStart + = resultStart / resultCountPerPage >= 5 + ? resultStart / resultCountPerPage - 4 + : 0; + unsigned int pageCount + = estimatedResultCount / resultCountPerPage + 1 - pageStart; + + if (pageCount > 10) { + pageCount = 10; + } else if (pageCount == 1) { + pageCount = 0; + } + + for (unsigned int i = pageStart; i < pageStart + pageCount; i++) { + kainjow::mustache::data page; + page.set("label", to_string(i + 1)); + page.set("start", to_string(i * resultCountPerPage)); + page.set("end", to_string((i + 1) * resultCountPerPage)); + + if (i * resultCountPerPage == resultStart) { + page.set("selected", true); + } + pages.push_back(page); + } + + std::string template_str = RESOURCE::templates::search_result_html; + kainjow::mustache::mustache tmpl(template_str); + + kainjow::mustache::data allData; + allData.set("results", results); + allData.set("pages", pages); + allData.set("hasResult", estimatedResultCount != 0); + allData.set("count", kiwix::beautifyInteger(estimatedResultCount)); + allData.set("searchPattern", kiwix::encodeDiples(this->searchPattern)); + allData.set("searchPatternEncoded", urlEncode(this->searchPattern)); + allData.set("resultStart", to_string(resultStart + 1)); + allData.set("resultEnd", to_string(min(resultEnd, estimatedResultCount))); + allData.set("resultRange", to_string(resultCountPerPage)); + allData.set("resultLastPageStart", to_string(estimatedResultCount > resultCountPerPage + ? round(estimatedResultCount / resultCountPerPage) * resultCountPerPage + : 0)); + allData.set("lastResult", to_string(estimatedResultCount)); + allData.set("protocolPrefix", this->protocolPrefix); + allData.set("searchProtocolPrefix", this->searchProtocolPrefix); + allData.set("contentId", this->searchContent); + + std::stringstream ss; + tmpl.render(allData, [&ss](const std::string& str) { ss << str; }); + return ss.str(); +} + +} diff --git a/src/searcher.cpp b/src/searcher.cpp index 4b9125ed5..4d603d1d6 100644 --- a/src/searcher.cpp +++ b/src/searcher.cpp @@ -65,16 +65,12 @@ struct SearcherInternal { }; /* Constructor */ -Searcher::Searcher(const std::string& humanReadableName) +Searcher::Searcher() : internal(new SearcherInternal()), searchPattern(""), - protocolPrefix("zim://"), - searchProtocolPrefix("search://?"), - resultCountPerPage(0), estimatedResultCount(0), resultStart(0), - resultEnd(0), - contentHumanReadableId(humanReadableName) + resultEnd(0) { loadICUExternalTables(); } @@ -85,16 +81,21 @@ Searcher::~Searcher() delete internal; } -bool Searcher::add_reader(Reader* reader, const std::string& humanReadableName) +bool Searcher::add_reader(Reader* reader) { if (!reader->hasFulltextIndex()) { return false; } this->readers.push_back(reader); - this->humanReaderNames.push_back(humanReadableName); return true; } + +Reader* Searcher::get_reader(int readerIndex) +{ + return readers.at(readerIndex); +} + /* Search strings in the database */ void Searcher::search(std::string& search, unsigned int resultStart, @@ -233,18 +234,6 @@ unsigned int Searcher::getEstimatedResultCount() return this->estimatedResultCount; } -bool Searcher::setProtocolPrefix(const std::string prefix) -{ - this->protocolPrefix = prefix; - return true; -} - -bool Searcher::setSearchProtocolPrefix(const std::string prefix) -{ - this->searchProtocolPrefix = prefix; - return true; -} - _Result::_Result(zim::Search::iterator& iterator) : iterator(iterator) { @@ -286,79 +275,5 @@ int _Result::get_readerIndex() return iterator.get_fileIndex(); } -string Searcher::getHtml() -{ - kainjow::mustache::data results{kainjow::mustache::data::type::list}; - - this->restart_search(); - Result* p_result = NULL; - while ((p_result = this->getNextResult())) { - kainjow::mustache::data result; - result.set("title", p_result->get_title()); - result.set("url", p_result->get_url()); - result.set("snippet", p_result->get_snippet()); - result.set("resultContentId", humanReaderNames[p_result->get_readerIndex()]); - - if (p_result->get_wordCount() >= 0) { - result.set("wordCount", kiwix::beautifyInteger(p_result->get_wordCount())); - } - - results.push_back(result); - delete p_result; - } - - // pages - kainjow::mustache::data pages{kainjow::mustache::data::type::list}; - - unsigned int pageStart - = this->resultStart / this->resultCountPerPage >= 5 - ? this->resultStart / this->resultCountPerPage - 4 - : 0; - unsigned int pageCount - = this->estimatedResultCount / this->resultCountPerPage + 1 - pageStart; - - if (pageCount > 10) { - pageCount = 10; - } else if (pageCount == 1) { - pageCount = 0; - } - - for (unsigned int i = pageStart; i < pageStart + pageCount; i++) { - kainjow::mustache::data page; - page.set("label", to_string(i + 1)); - page.set("start", to_string(i * this->resultCountPerPage)); - page.set("end", to_string((i + 1) * this->resultCountPerPage)); - - if (i * this->resultCountPerPage == this->resultStart) { - page.set("selected", true); - } - pages.push_back(page); - } - - std::string template_str = RESOURCE::search_result_tmpl; - kainjow::mustache::mustache tmpl(template_str); - - kainjow::mustache::data allData; - allData.set("results", results); - allData.set("pages", pages); - allData.set("hasResult", this->estimatedResultCount != 0); - allData.set("count", kiwix::beautifyInteger(this->estimatedResultCount)); - allData.set("searchPattern", kiwix::encodeDiples(this->searchPattern)); - allData.set("searchPatternEncoded", urlEncode(this->searchPattern)); - allData.set("resultStart", to_string(this->resultStart + 1)); - allData.set("resultEnd", to_string(min(this->resultEnd, this->estimatedResultCount))); - allData.set("resultRange", to_string(this->resultCountPerPage)); - allData.set("resultLastPageStart", to_string(this->estimatedResultCount > this->resultCountPerPage - ? round(this->estimatedResultCount / this->resultCountPerPage) * this->resultCountPerPage - : 0)); - allData.set("lastResult", to_string(this->estimatedResultCount)); - allData.set("protocolPrefix", this->protocolPrefix); - allData.set("searchProtocolPrefix", this->searchProtocolPrefix); - allData.set("contentId", this->contentHumanReadableId); - - std::stringstream ss; - tmpl.render(allData, [&ss](const std::string& str) { ss << str; }); - return ss.str(); -} } diff --git a/static/meson.build b/static/meson.build index 44f5c3004..353f5266e 100644 --- a/static/meson.build +++ b/static/meson.build @@ -7,5 +7,5 @@ lib_resources = custom_target('resources', '--hfile', '@OUTPUT1@', '--source_dir', '@OUTDIR@', '@INPUT@'], - depend_files: files('search_result.tmpl') + build_always_stale: true ) diff --git a/static/resources_list.txt b/static/resources_list.txt index 0b64bf339..eb47f4479 100644 --- a/static/resources_list.txt +++ b/static/resources_list.txt @@ -1 +1 @@ -search_result.tmpl +templates/search_result.html diff --git a/static/search_result.tmpl b/static/templates/search_result.html similarity index 100% rename from static/search_result.tmpl rename to static/templates/search_result.html