211 خطوط
6.4 KiB
C++
211 خطوط
6.4 KiB
C++
/* Copyright (c) 2018 Albert S.
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in all
|
|
copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
SOFTWARE.
|
|
*/
|
|
#include "handlerpageview.h"
|
|
#include "../database/exceptions.h"
|
|
#include "../logger.h"
|
|
#include "../parser.h"
|
|
#include "../htmllink.h"
|
|
#include "../dynamic/dynamiccontentpostlist.h"
|
|
#include "../dynamic/dynamiccontentincludepage.h"
|
|
bool HandlerPageView::canAccess(std::string page)
|
|
{
|
|
return effectivePermissions(page).canRead();
|
|
}
|
|
|
|
std::string HandlerPageView::accessErrorMessage()
|
|
{
|
|
return "You don't have permission to view this page";
|
|
}
|
|
|
|
std::string HandlerPageView::createIndexContent(IParser &parser, std::string content)
|
|
{
|
|
std::vector<Headline> headlines = parser.extractHeadlines(content);
|
|
std::string indexcontent = "";
|
|
unsigned int previous = 0;
|
|
for(const Headline &h : headlines)
|
|
{
|
|
if(h.level > previous)
|
|
{
|
|
indexcontent += "<ul>";
|
|
}
|
|
else if(h.level < previous)
|
|
{
|
|
unsigned int diff = previous - h.level;
|
|
for(unsigned int i = 0; i < diff; i++)
|
|
{
|
|
indexcontent += "</ul>";
|
|
}
|
|
}
|
|
previous = h.level;
|
|
HtmlLink link;
|
|
link.href = "#" + h.title;
|
|
link.innervalue = h.title;
|
|
link.cssclass = "indexlink";
|
|
indexcontent += "<li>" + link.render() + "</li>";
|
|
}
|
|
for(unsigned int i = 0; i < previous; i++)
|
|
{
|
|
indexcontent += "</ul>";
|
|
}
|
|
return indexcontent;
|
|
}
|
|
|
|
Response HandlerPageView::handleRequest(PageDao &pageDao, std::string pagename, const Request &r)
|
|
{
|
|
|
|
std::string revisionparam = r.get("revision");
|
|
unsigned int revisionid = 0;
|
|
if(!revisionparam.empty())
|
|
{
|
|
try
|
|
{
|
|
revisionid = utils::toUInt(revisionparam);
|
|
}
|
|
catch(const std::exception &e)
|
|
{
|
|
return errorResponse("Error", "Supplied revisionid is misformated");
|
|
}
|
|
}
|
|
|
|
std::optional<Revision> revision;
|
|
std::string templatepartname;
|
|
try
|
|
{
|
|
if(revisionid > 0)
|
|
{
|
|
if(!effectivePermissions(pagename).canSeePageHistory())
|
|
{
|
|
auto current = this->database->createRevisionDao()->getCurrentForPage(pagename);
|
|
if(current && current->revision > revisionid)
|
|
{
|
|
return errorResponse("Error", "You are not allowed to view older revisions of this page");
|
|
}
|
|
}
|
|
revision = this->database->createRevisionDao()->getRevisionForPage(pagename, revisionid);
|
|
if(!revision)
|
|
{
|
|
return errorResponse("Revision not found", "No such revision found");
|
|
}
|
|
templatepartname = "page_view_revision";
|
|
}
|
|
else
|
|
{
|
|
if(!this->userSession->loggedIn)
|
|
{
|
|
auto content = this->cache->get("page:foranon:" + pagename);
|
|
if(content)
|
|
{
|
|
Response r;
|
|
r.setBody(*content);
|
|
// TODO: etag?
|
|
return r;
|
|
}
|
|
}
|
|
revision = this->database->createRevisionDao()->getCurrentForPage(pagename);
|
|
templatepartname = "page_view";
|
|
}
|
|
}
|
|
catch(const DatabaseException &e)
|
|
{
|
|
Logger::error() << "DatabaseException in handlerpageview: " << e.what();
|
|
return errorResponse("Database error", "While trying to fetch revision, a database error occured");
|
|
}
|
|
|
|
TemplatePage page = this->templ->getPage(templatepartname);
|
|
|
|
Parser parser;
|
|
Response result;
|
|
result.setStatus(200);
|
|
std::string indexcontent;
|
|
std::string parsedcontent;
|
|
|
|
bool keepParsing = true;
|
|
|
|
std::function<std::string(std::string_view, std::string_view)> dynamicParseCallback =
|
|
[&](std::string_view key, std::string_view value) -> std::string
|
|
{
|
|
if(key == "dynamic:postlist")
|
|
{
|
|
std::shared_ptr<DynamicContentPostList> postlist = createDynamic<DynamicContentPostList>();
|
|
postlist->setArgument(std::string(value));
|
|
return postlist->render();
|
|
}
|
|
if(key == "dynamic:includepage")
|
|
{
|
|
std::shared_ptr<DynamicContentIncludePage> includePage = createDynamic<DynamicContentIncludePage>();
|
|
includePage->setArgument(std::string(value));
|
|
return parser.parse(pageDao, *this->urlProvider, includePage->render(), dynamicParseCallback);
|
|
}
|
|
return std::string{};
|
|
};
|
|
if(revisionid > 0)
|
|
{
|
|
indexcontent = createIndexContent(parser, revision->content);
|
|
parsedcontent = parser.parse(pageDao, *this->urlProvider, revision->content, dynamicParseCallback);
|
|
}
|
|
else
|
|
{
|
|
std::string cachekeyindexcontent = "page:indexcontent:" + pagename;
|
|
std::string cachekeyparsedcontent = "page:parsedcontent:" + pagename;
|
|
auto cachedindexcontent = this->cache->get(cachekeyindexcontent);
|
|
auto cachedparsedcontent = this->cache->get(cachekeyparsedcontent);
|
|
if(cachedindexcontent)
|
|
{
|
|
indexcontent = *cachedindexcontent;
|
|
}
|
|
else
|
|
{
|
|
indexcontent = createIndexContent(parser, revision->content);
|
|
this->cache->put(cachekeyindexcontent, indexcontent);
|
|
}
|
|
if(cachedparsedcontent)
|
|
{
|
|
parsedcontent = *cachedparsedcontent;
|
|
}
|
|
else
|
|
{
|
|
parsedcontent = parser.parse(pageDao, *this->urlProvider, revision->content, dynamicParseCallback);
|
|
this->cache->put(cachekeyparsedcontent, parsedcontent);
|
|
}
|
|
}
|
|
std::string revisionstr = std::to_string(revision->revision);
|
|
std::string customtitle = parser.extractCommand("pagetitle", revision->content);
|
|
page.setVar("content", parsedcontent);
|
|
page.setVar("index", indexcontent);
|
|
page.setVar("editedby", revision->author);
|
|
page.setVar("editedon", utils::toISODateTime(revision->timestamp));
|
|
page.setVar("historyurl", this->urlProvider->pageHistory(pagename));
|
|
page.setVar("revision", revisionstr);
|
|
setPageVars(page, pagename);
|
|
if(!customtitle.empty())
|
|
{
|
|
page.setVar("title", createPageTitle(customtitle));
|
|
}
|
|
std::string body = page.render();
|
|
if(revisionid == 0 && !this->userSession->loggedIn)
|
|
{
|
|
this->cache->put("page:foranon:" + pagename, body);
|
|
}
|
|
result.addHeader("ETAG", revisionstr + "+" + std::to_string(this->userSession->loggedIn));
|
|
result.setBody(body);
|
|
return result;
|
|
}
|