diff --git a/include/opds_dumper.h b/include/opds_dumper.h index c6cc60d3d..72d30ef4a 100644 --- a/include/opds_dumper.h +++ b/include/opds_dumper.h @@ -51,11 +51,26 @@ class OPDSDumper /** * Dump the OPDS feed. * - * @param id The id of the library. + * @param bookIds the ids of the books to include in the feed * @return The OPDS feed. */ std::string dumpOPDSFeed(const std::vector& bookIds); + /** + * Dump the OPDS feed. + * + * @param bookIds the ids of the books to include in the feed + * @return The OPDS feed. + */ + std::string dumpOPDSFeedV2(const std::vector& bookIds, const std::string& query) const; + + /** + * Set the id of the library. + * + * @param id the id to use. + */ + void setLibraryId(const std::string& id) { this->libraryId = id;} + /** * Set the id of the opds stream. * @@ -103,6 +118,7 @@ class OPDSDumper protected: kiwix::Library* library; std::string id; + std::string libraryId; std::string title; std::string date; std::string rootLocation; diff --git a/include/tools/otherTools.h b/include/tools/otherTools.h index 4b7d2e184..c105d52f5 100644 --- a/include/tools/otherTools.h +++ b/include/tools/otherTools.h @@ -24,6 +24,7 @@ #include #include #include +#include namespace pugi { class xml_node; @@ -47,6 +48,9 @@ namespace kiwix MimeCounterType parseMimetypeCounter(const std::string& counterData); std::string gen_date_str(); + std::string gen_uuid(const std::string& s); + + std::string render_template(const std::string& template_str, kainjow::mustache::data data); } #endif diff --git a/src/opds_dumper.cpp b/src/opds_dumper.cpp index 2147e0866..c20d492e9 100644 --- a/src/opds_dumper.cpp +++ b/src/opds_dumper.cpp @@ -22,6 +22,9 @@ #include "tools/otherTools.h" +#include "kiwixlib-resources.h" +#include + namespace kiwix { /* Constructor */ @@ -135,4 +138,45 @@ string OPDSDumper::dumpOPDSFeed(const std::vector& bookIds) return nodeToString(root_node); } +typedef kainjow::mustache::data MustacheData; + +string OPDSDumper::dumpOPDSFeedV2(const std::vector& bookIds, const std::string& query) const +{ + kainjow::mustache::list bookData; + for ( const auto& bookId : bookIds ) { + const Book& book = library->getBookById(bookId); + const MustacheData bookUrl = book.getUrl().empty() + ? MustacheData(false) + : MustacheData(book.getUrl()); + bookData.push_back(kainjow::mustache::object{ + {"id", "urn:uuid:"+book.getId()}, + {"name", book.getName()}, + {"title", book.getTitle()}, + {"description", book.getDescription()}, + {"language", book.getLanguage()}, + {"content_id", book.getHumanReadableIdFromPath()}, + {"updated", book.getDate() + "T00:00:00Z"}, + {"category", book.getCategory()}, + {"flavour", book.getFlavour()}, + {"tags", book.getTags()}, + {"article_count", to_string(book.getArticleCount())}, + {"media_count", to_string(book.getMediaCount())}, + {"author_name", book.getCreator()}, + {"publisher_name", book.getPublisher()}, + {"url", bookUrl}, + {"size", to_string(book.getSize())}, + }); + } + + const kainjow::mustache::object template_data{ + {"date", gen_date_str()}, + {"endpoint_root", rootLocation + "/catalog/v2"}, + {"feed_id", gen_uuid(libraryId + "/entries?"+query)}, + {"filter", query.empty() ? MustacheData(false) : MustacheData(query)}, + {"books", bookData } + }; + + return render_template(RESOURCE::catalog_v2_entries_xml, template_data); +} + } diff --git a/src/server/internalServer.cpp b/src/server/internalServer.cpp index 9520af296..77724e669 100644 --- a/src/server/internalServer.cpp +++ b/src/server/internalServer.cpp @@ -79,11 +79,6 @@ namespace kiwix { namespace { -inline std::string gen_uuid(const std::string& s) -{ - return to_string(zim::Uuid::generate(s)); -} - inline std::string normalizeRootUrl(const std::string& rootUrl) { return (rootUrl.empty() || rootUrl[0] == '/') @@ -767,53 +762,17 @@ std::unique_ptr InternalServer::handle_catalog_v2_root(const RequestCo std::unique_ptr InternalServer::handle_catalog_v2_entries(const RequestContext& request) { - const std::string root_url = normalizeRootUrl(m_root); - - const auto now = gen_date_str(); - kainjow::mustache::list bookData; const auto filter = get_search_filter(request); - const auto allMatchingEntries = mp_library->filter(filter); const size_t count = request.get_optional_param("count", 10UL); const size_t start = request.get_optional_param("start", 0UL); - for ( const auto& bookId : subrange(allMatchingEntries, start, count) ) { - const Book& book = mp_library->getBookById(bookId); - const MustacheData bookUrl = book.getUrl().empty() - ? MustacheData(false) - : MustacheData(book.getUrl()); - bookData.push_back(kainjow::mustache::object{ - {"id", "urn:uuid:"+book.getId()}, - {"name", book.getName()}, - {"title", book.getTitle()}, - {"description", book.getDescription()}, - {"language", book.getLanguage()}, - {"content_id", book.getHumanReadableIdFromPath()}, - {"updated", book.getDate() + "T00:00:00Z"}, - {"category", book.getCategory()}, - {"flavour", book.getFlavour()}, - {"tags", book.getTags()}, - {"article_count", to_string(book.getArticleCount())}, - {"media_count", to_string(book.getMediaCount())}, - {"author_name", book.getCreator()}, - {"publisher_name", book.getPublisher()}, - {"url", bookUrl}, - {"size", to_string(book.getSize())}, - }); - } - - const auto query = request.get_query().empty() - ? MustacheData(false) - : MustacheData(request.get_query()); - + const auto bookIds = subrange(mp_library->filter(filter), start, count); + OPDSDumper opdsDumper(mp_library); + opdsDumper.setRootLocation(normalizeRootUrl(m_root)); + opdsDumper.setLibraryId(m_library_id); + const auto opdsFeed = opdsDumper.dumpOPDSFeedV2(bookIds, request.get_query()); return ContentResponse::build( *this, - RESOURCE::catalog_v2_entries_xml, - kainjow::mustache::object{ - {"date", now}, - {"endpoint_root", root_url + "/catalog/v2"}, - {"feed_id", gen_uuid(m_library_id + "/entries?"+request.get_query())}, - {"filter", query}, - {"books", bookData } - }, + opdsFeed, "application/atom+xml;profile=opds-catalog;kind=acquisition" ); } diff --git a/src/server/response.cpp b/src/server/response.cpp index 3ebec43d2..1f0a8f401 100644 --- a/src/server/response.cpp +++ b/src/server/response.cpp @@ -24,6 +24,7 @@ #include "tools/regexTools.h" #include "tools/stringTools.h" +#include "tools/otherTools.h" #include "string.h" #include @@ -38,17 +39,6 @@ namespace { // some utilities -std::string render_template(const std::string& template_str, kainjow::mustache::data data) -{ - kainjow::mustache::mustache tmpl(template_str); - kainjow::mustache::data urlencode{kainjow::mustache::lambda2{ - [](const std::string& str,const kainjow::mustache::renderer& r) { return urlEncode(r(str), true); }}}; - data.set("urlencoded", urlencode); - std::stringstream ss; - tmpl.render(data, [&ss](const std::string& str) { ss << str; }); - return ss.str(); -} - std::string get_mime_type(const zim::Item& item) { try { diff --git a/src/tools/otherTools.cpp b/src/tools/otherTools.cpp index a51ed5ff3..eb081efb7 100644 --- a/src/tools/otherTools.cpp +++ b/src/tools/otherTools.cpp @@ -33,6 +33,8 @@ #include #include +#include + static std::map codeisomapping { { "aa", "aar" }, @@ -358,3 +360,19 @@ std::string kiwix::gen_date_str() << std::setw(2) << std::setfill('0') << tm->tm_sec << "Z"; return is.str(); } + +std::string kiwix::gen_uuid(const std::string& s) +{ + return kiwix::to_string(zim::Uuid::generate(s)); +} + +std::string kiwix::render_template(const std::string& template_str, kainjow::mustache::data data) +{ + kainjow::mustache::mustache tmpl(template_str); + kainjow::mustache::data urlencode{kainjow::mustache::lambda2{ + [](const std::string& str,const kainjow::mustache::renderer& r) { return urlEncode(r(str), true); }}}; + data.set("urlencoded", urlencode); + std::stringstream ss; + tmpl.render(data, [&ss](const std::string& str) { ss << str; }); + return ss.str(); +}