159 строки
5.2 KiB
C++
159 строки
5.2 KiB
C++
#include "handlerfeedgenerator.h"
|
|
#include "../revisionrenderer.h"
|
|
std::vector<HandlerFeedGenerator::EntryRevisionPair> HandlerFeedGenerator::fetchEntries(
|
|
std::vector<std::string> categories)
|
|
{
|
|
auto revisionDao = this->database->createRevisionDao();
|
|
auto pageDao = this->database->createPageDao();
|
|
auto permissionDao = this->database->createPermissionsDao();
|
|
|
|
std::vector<EntryRevisionPair> result;
|
|
QueryOption option;
|
|
option.includeUnlisted = true;
|
|
// option.limit = 20;
|
|
|
|
auto comppage = [](const Page &a, const Page &b) { return a.name < b.name; };
|
|
std::set<Page, decltype(comppage)> 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)
|
|
{
|
|
if(member.feedlisted)
|
|
{
|
|
Permissions perms = permissionDao->find(member.name, this->userSession->user.login)
|
|
.value_or(this->userSession->user.permissions);
|
|
if(perms.canRead())
|
|
{
|
|
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<HandlerFeedGenerator::EntryRevisionPair> &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, *this->userSession};
|
|
|
|
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", 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;
|
|
}
|