#include "handlerfeedgenerator.h" #include "../parser.h" #include "../revisionrenderer.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; auto comppage = [](const Page &a, const Page &b) { return a.name < b.name; }; std::set members (comppage); 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) { if(!categoryDao->find(cat)) { throw std::runtime_error("No such category"); } auto catmembers = categoryDao->fetchMembers(cat, option); std::copy(catmembers.begin(), catmembers.end(), std::inserter(members, members.end())); } } for(const Page &member : members) { auto revision = revisionDao->getRevisionForPage(member.name, 1).value(); result.push_back({member, 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; } std::string 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(); std::string subtitle = filter; if(utils::trim(filter).empty()) { subtitle = "All pages"; } RevisionRenderer revisionRenderer { *this->templ, *this->database, *this->urlProvider }; 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 entryUpdated = utils::formatLocalDate(current.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); atomentry.setVar("entryupdated", entryUpdated); atomentry.setVar("entrycontent", utils::html_xss(revisionRenderer.renderContent(current, page.title))); stream << atomentry.render(); } stream << atomfooter; TemplatePage atomheader = this->templ->getPage("feeds/atomheader"); atomheader.setVar("subtitle", subtitle); std::string selflink = utils::html_xss(this->urlProvider->atomFeed(filter)); atomheader.setVar("atomfeeduniqueid", selflink); atomheader.setVar("atomselflink", selflink); atomheader.setVar("atomfeedupdate", utils::formatLocalDate(newestPublished, dateformat) + "Z"); return atomheader.render() + stream.str(); } Response HandlerFeedGenerator::handleRequest(const Request &r) { Response response; try { std::string type = r.get("type"); std::string categories = r.get("cats"); if(type == "atom") { std::string filter = categories; Response result; result.setStatus(200); result.setContentType("application/atom+xml"); std::string cacheKey = "feed:atom:" + filter; auto cached = this->cache->get(cacheKey); if(cached) { result.setBody(cached.value()); } else { auto entries = fetchEntries(utils::split(categories, ',')); std::string feed = generateAtom(entries, filter); result.setBody(feed); this->cache->put(cacheKey, feed); } response = result; } 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; }