From 4854ea85f20266a80f807effb4542008c7087d5b Mon Sep 17 00:00:00 2001 From: Albert S Date: Sun, 27 Mar 2022 19:48:57 +0200 Subject: [PATCH] Begin HandlerFeedGenerator: Generates Atom feeds for categories (or all pages) --- handlers/handlerfeedgenerator.cpp | 129 ++++++++++++++++++++++++++++++ handlers/handlerfeedgenerator.h | 21 +++++ 2 files changed, 150 insertions(+) create mode 100644 handlers/handlerfeedgenerator.cpp create mode 100644 handlers/handlerfeedgenerator.h diff --git a/handlers/handlerfeedgenerator.cpp b/handlers/handlerfeedgenerator.cpp new file mode 100644 index 0000000..c87a685 --- /dev/null +++ b/handlers/handlerfeedgenerator.cpp @@ -0,0 +1,129 @@ +#include "handlerfeedgenerator.h" +#include "../parser.h" +std::vector HandlerFeedGenerator::fetchEntries( + std::vector categories) +{ + auto revisionDao = this->database->createRevisionDao(); + auto pageDao = this->database->createPageDao(); + + std::vector result; + QueryOption option; + option.includeInvisible = false; + // option.limit = 20; + std::set members; + if(categories.empty()) + { + auto pages = pageDao->getPageList(option); + std::copy(pages.begin(), pages.end(), std::inserter(members, members.end())); + } + else + { + auto categoryDao = this->database->createCategoryDao(); + for(std::string cat : categories) + { + auto catmembers = categoryDao->fetchMembers(cat, option); + std::copy(catmembers.begin(), catmembers.end(), std::inserter(members, members.end())); + } + } + for(const std::string &member : members) + { + auto page = pageDao->find(member).value(); + auto revision = revisionDao->getRevisionForPage(page.name, 1).value(); + result.push_back({page, revision}); + } + std::sort(result.begin(), result.end(), + [](EntryRevisionPair &a, EntryRevisionPair &b) { return a.second.timestamp > b.second.timestamp; }); + + const int maxResults = 20; + if(result.size() > maxResults) + { + result.erase(result.begin() + maxResults - 1, result.end()); + } + + return result; +} + +Response HandlerFeedGenerator::generateAtom(const std::vector &entries, + std::string filter) +{ + + std::stringstream stream; + // don't care about offset for now especially since "%z" does not return what we need exactly (with ':') + const std::string dateformat = "%Y-%m-%dT%T"; + + time_t newestPublished = 0; + std::string atomfooter = this->templ->loadResolvedPart("feeds/atomfooter"); + auto revisionDao = this->database->createRevisionDao(); + auto pageDao = this->database->createPageDao(); + + if(utils::trim(filter).empty()) + { + filter = "All pages"; + } + + for(const EntryRevisionPair &entry : entries) + { + const Page &page = entry.first; + const Revision &initialRevision = entry.second; + Revision current = revisionDao->getCurrentForPage(page.name).value(); + std::string url = this->urlProvider->rootUrl() + this->urlProvider->page(page.name); + if(initialRevision.timestamp > newestPublished) + { + newestPublished = initialRevision.timestamp; + } + std::string entryPublished = utils::formatLocalDate(initialRevision.timestamp, dateformat) + "Z"; + std::string entryurl = + this->urlProvider->combine({this->urlProvider->rootUrl(), this->urlProvider->page(page.name)}); + TemplatePage atomentry = this->templ->getPage("feeds/atomentry"); + atomentry.setVar("entrytitle", utils::html_xss(page.title)); + atomentry.setVar("entryurl", utils::html_xss(entryurl)); + atomentry.setVar("entryid", utils::html_xss(entryurl)); + atomentry.setVar("entrypublished", entryPublished); + Parser parser; + atomentry.setVar("entrycontent", utils::html_xss(parser.parse(*pageDao, *this->urlProvider, current.content))); + stream << atomentry.render(); + } + stream << atomfooter; + TemplatePage atomheader = this->templ->getPage("feeds/atomheader"); + atomheader.setVar("subtitle", filter); + atomheader.setVar("atomfeeduniqueid", utils::html_xss(this->urlProvider->atomFeed(filter))); + atomheader.setVar("atomfeedupdate", utils::formatLocalDate(newestPublished, dateformat) + "Z"); + + Response result; + result.setStatus(200); + result.setContentType("application/atom+xml"); + result.setBody(atomheader.render() + stream.str()); + return result; +} + +Response HandlerFeedGenerator::handleRequest(const Request &r) +{ + Response response; + try + { + std::string type = r.get("type"); + std::string categories = r.get("cats"); + + auto entries = fetchEntries(utils::split(categories, ',')); + if(type == "atom") + { + std::string filter = categories; + response = generateAtom(entries, filter); + } + else + { + return errorResponse("Invalid feed type", "Unknown feed type, try atom", 400); + } + } + catch(std::runtime_error &e) + { + Logger::error() << "Error while serving feed: " << e.what(); + return errorResponse("Error", "An error occured while trying to serve the feed", 500); + } + return response; +} + +bool HandlerFeedGenerator::canAccess(const Permissions &perms) +{ + return true; +} diff --git a/handlers/handlerfeedgenerator.h b/handlers/handlerfeedgenerator.h new file mode 100644 index 0000000..5d803b0 --- /dev/null +++ b/handlers/handlerfeedgenerator.h @@ -0,0 +1,21 @@ +#ifndef HANDLERFEEDGENERATOR_H +#define HANDLERFEEDGENERATOR_H +#include "handler.h" +#include "../page.h" +#include "../revision.h" +class HandlerFeedGenerator : public Handler +{ + typedef std::pair EntryRevisionPair; + + protected: + std::vector fetchEntries(std::vector categories); + Response generateAtom(const std::vector &entries, std::string atomtitle); + Response generateRss(const std::vector &entries); + + public: + using Handler::Handler; + Response handleRequest(const Request &r) override; + bool canAccess(const Permissions &perms) override; +}; + +#endif // HANDLERFEEDGENERATOR_H