aboutsummaryrefslogtreecommitdiff
path: root/src/content
diff options
context:
space:
mode:
Diffstat (limited to 'src/content')
-rw-r--r--src/content/blog/Blog.cpp138
-rw-r--r--src/content/blog/Blog.hpp28
-rw-r--r--src/content/markdown/Markdown.cpp47
-rw-r--r--src/content/markdown/Markdown.hpp11
-rw-r--r--src/content/static/Static.cpp12
-rw-r--r--src/content/static/Static.hpp1
6 files changed, 214 insertions, 23 deletions
diff --git a/src/content/blog/Blog.cpp b/src/content/blog/Blog.cpp
index b473bf3..1893cc5 100644
--- a/src/content/blog/Blog.cpp
+++ b/src/content/blog/Blog.cpp
@@ -1,11 +1,13 @@
#include "Blog.hpp"
+#include <filesystem>
+
Blog::Blog(std::string uriBasePath, std::string mainTemplatePath, std::string blogPath)
: Content(),
m_UriBasePath(uriBasePath),
m_MainTemplatePath(mainTemplatePath),
m_BlogPath(blogPath),
- m_BlogEntries("", blogPath)
+ m_BlogContents("", blogPath)
{
m_Redirections.push_back(uriBasePath + "/");
m_Redirections.push_back(uriBasePath + "/index.html");
@@ -13,25 +15,48 @@ Blog::Blog(std::string uriBasePath, std::string mainTemplatePath, std::string bl
bool Blog::Init()
{
- std::cout << "Blog entries path: " << m_BlogPath << std::endl;
+ bool retval = true;
+ std::cout << "Blog entries path: " << m_BlogPath << std::endl;
std::vector<std::string> extensions = {"json"};
- if (m_BlogEntriesMetadata.Scan(m_BlogPath, extensions, false) == false)
+ Filesystem fs;
+ if (fs.Scan(m_BlogPath, extensions, false) == false)
{
return false;
}
- m_BlogEntries.Init();
+ for (auto const & jfile : fs.GetFiles())
+ {
+ auto const json_metadata = inja::json::parse(jfile.second.data);
+ BlogEntry be =
+ std::make_shared<struct blog_entry>(jfile.first,
+ std::string(std::filesystem::path(jfile.first).stem()) + ".md");
+ if (Blog::ValidateAndSetMetdadata(json_metadata, be) == false)
+ {
+ std::cerr << "Blog Metadata validation failed." << std::endl;
+ retval = false;
+ }
+ m_BlogEntriesSortedByDate.push_back(be);
- return true;
+ m_Redirections.push_back(std::filesystem::path(jfile.first).stem());
+ }
+
+ m_BlogContents.Init();
+
+ if (retval == false)
+ {
+ return false;
+ }
+
+ return ValidateEntries();
}
void Blog::Shutdown()
{
std::cout << "Blog module shutdown" << std::endl;
- m_BlogEntries.Shutdown();
+ m_BlogContents.Shutdown();
}
bool Blog::Render(RequestResponse & rr, RenderData & rd, std::string & out)
@@ -40,7 +65,15 @@ bool Blog::Render(RequestResponse & rr, RenderData & rd, std::string & out)
(void)rd;
(void)out;
- rd["blah"] = "Yooooh!";
+ if (rr.GetUriPath() == m_UriBasePath || rr.GetUriPath() == m_UriBasePath + "/" ||
+ rr.GetUriPath() == m_UriBasePath + "/index.html")
+ {
+ GenerateBlogListing(rd["blog_listing"]);
+ }
+ else
+ {
+ rd["blog_content"] = "bla";
+ }
return true;
}
@@ -59,3 +92,94 @@ Redirections const & Blog::GetRedirections() const
{
return m_Redirections;
}
+
+bool Blog::ValidateAndSetMetdadata(BlogMetadata const & blogMetadata, BlogEntry & blogEntry)
+{
+ bool retval = true;
+ std::function<bool(BlogMetadata const &, std::string const)> validateMetadata =
+ [blogEntry](BlogMetadata const & bm, std::string const tname) {
+ if (bm.find(tname) == bm.cend())
+ {
+ std::cerr << "Metadata validation: JSON key '" << tname << "' missing in "
+ << blogEntry->metadata_filename << std::endl;
+ return false;
+ }
+ return true;
+ };
+ std::function<bool(std::string const &, std::time_t &)> parseDateTime = [](std::string const & timeStr,
+ std::time_t & time) {
+ std::tm tm = {};
+ std::stringstream ss(timeStr);
+ ss >> std::get_time(&tm, "%d.%m.%y %H:%M");
+ time = std::mktime(&tm);
+ if (time <= 0)
+ {
+ std::cerr << "Metadata validation: Invalid time string '" << timeStr
+ << "', format required: '%d.%m.%y %H:%M'" << std::endl;
+ return false;
+ }
+ return true;
+ };
+
+ if (validateMetadata(blogMetadata, "createDate") == false ||
+ parseDateTime(blogMetadata["createDate"], blogEntry->createDate) == false)
+ {
+ retval = false;
+ }
+
+ if (validateMetadata(blogMetadata, "publishDate") == false ||
+ parseDateTime(blogMetadata["publishDate"], blogEntry->publishDate) == false)
+ {
+ retval = false;
+ }
+
+ if (validateMetadata(blogMetadata, "published") == false)
+ {
+ retval = false;
+ }
+ blogEntry->published = blogMetadata["published"];
+
+ return retval;
+}
+
+bool Blog::ValidateEntries() const
+{
+ bool retval = true;
+
+ for (auto const & e : m_BlogEntriesSortedByDate)
+ {
+ if (m_BlogContents.HasMarkdownFile(e->content_filename) == false)
+ {
+ std::cerr << "Blog entry metadata " << e->metadata_filename << " exists, but markdown file "
+ << e->content_filename << " not." << std::endl;
+ retval = false;
+ }
+ }
+ for (auto const & m : m_BlogContents.GetMarkdowns())
+ {
+ if (std::any_of(m_BlogEntriesSortedByDate.cbegin(),
+ m_BlogEntriesSortedByDate.cend(),
+ [m](BlogEntry const & be) { return m.first == be->content_filename; }) == false)
+ {
+ std::cerr << "Blog entry markdown " << m.first << " exists, but metadata not." << std::endl;
+ retval = false;
+ }
+ }
+
+ return retval;
+}
+
+void Blog::GenerateBlogListing(RenderData & rd) const
+{
+ for (auto const & e : m_BlogEntriesSortedByDate)
+ {
+ RenderData re;
+ re["metadata_filename"] = e->metadata_filename;
+ re["content_filename"] = e->content_filename;
+ re["createDate"] = e->createDate;
+ re["publishDate"] = e->publishDate;
+ re["published"] = e->published;
+
+ rd += re;
+ }
+}
diff --git a/src/content/blog/Blog.hpp b/src/content/blog/Blog.hpp
index 970c7fd..a0e35b1 100644
--- a/src/content/blog/Blog.hpp
+++ b/src/content/blog/Blog.hpp
@@ -1,10 +1,30 @@
#ifndef BLOG_H
#define BLOG_H 1
+#include <inja/inja.hpp>
+
#include "../../Content.hpp"
#include "../../Filesystem.hpp"
#include "../markdown/Markdown.hpp"
+struct blog_entry
+{
+ explicit blog_entry(std::string const & metadata_filename, std::string const & content_filename)
+ : metadata_filename(metadata_filename), content_filename(content_filename)
+ {
+ }
+
+ std::string const metadata_filename;
+ std::string const content_filename;
+ std::time_t createDate;
+ std::time_t publishDate;
+ bool published;
+};
+
+using BlogMetadata = inja::json;
+using BlogEntry = std::shared_ptr<struct blog_entry>;
+using BlogEntries = std::vector<BlogEntry>;
+
class Blog : public Content
{
public:
@@ -18,13 +38,17 @@ public:
std::string const & GetMainTemplate() const;
Redirections const & GetRedirections() const;
+ static bool ValidateAndSetMetdadata(BlogMetadata const & blogMetadata, BlogEntry & blogEntry);
+ bool ValidateEntries() const;
+ void GenerateBlogListing(RenderData & rd) const;
+
private:
std::string m_UriBasePath;
std::string m_MainTemplatePath;
std::string m_BlogPath;
Redirections m_Redirections;
- Filesystem m_BlogEntriesMetadata;
- Markdown m_BlogEntries;
+ Markdown m_BlogContents;
+ BlogEntries m_BlogEntriesSortedByDate;
};
#endif
diff --git a/src/content/markdown/Markdown.cpp b/src/content/markdown/Markdown.cpp
index 2009772..63358b4 100644
--- a/src/content/markdown/Markdown.cpp
+++ b/src/content/markdown/Markdown.cpp
@@ -1,7 +1,10 @@
#include "Markdown.hpp"
-Markdown::Markdown(std::string uriBasePath, std::string markdownFilesPath)
- : Content(), m_UriBasePath(uriBasePath), m_MainTemplatePath(""), m_MarkdownFilesPath(markdownFilesPath)
+Markdown::Markdown(std::string uriBasePath, std::string markdownFilesPath, std::string mainTemplatePath)
+ : Content(),
+ m_UriBasePath(uriBasePath),
+ m_MainTemplatePath(mainTemplatePath),
+ m_MarkdownFilesPath(markdownFilesPath)
{
}
@@ -11,11 +14,18 @@ bool Markdown::Init()
std::vector<std::string> extensions = {"md"};
- if (m_MarkdownFiles.Scan(m_MarkdownFilesPath, extensions, false) == false)
+ Filesystem fs;
+ if (fs.Scan(m_MarkdownFilesPath, extensions, false) == false)
{
return false;
}
+ for (auto const & mfile : fs.GetFiles())
+ {
+ m_Markdowns[mfile.first] =
+ std::make_shared<std::string>(std::string(mfile.second.data.begin(), mfile.second.data.end()));
+ }
+
return true;
}
@@ -26,13 +36,16 @@ void Markdown::Shutdown()
bool Markdown::Render(RequestResponse & rr, RenderData & rd, std::string & out)
{
+ (void)rr;
+ (void)rd;
(void)out;
- rd["blub"] = "Yoh21!";
- rr.UseOutputHeader();
- rr.AddOutputHeader("blaaaa", "blubb");
+ if (m_MainTemplatePath.empty() == true)
+ {
+ return false;
+ }
- return true;
+ return false; /* TODO: Make markdown module usable as standalone module?! */
}
std::string const & Markdown::GetUriBasePath() const
@@ -49,3 +62,23 @@ Redirections const & Markdown::GetRedirections() const
{
return m_Redirections;
}
+
+bool Markdown::HasMarkdownFile(std::string filePath) const
+{
+ return m_Markdowns.find(filePath) != m_Markdowns.end();
+}
+
+bool Markdown::HasMarkdownURI(std::string uriPath) const
+{
+ return HasMarkdownFile(uriPath.substr(m_UriBasePath.length() + 1, std::string::npos));
+}
+
+Markdowns const & Markdown::GetMarkdowns() const
+{
+ return m_Markdowns;
+}
+
+std::shared_ptr<std::string> const & Markdown::GetMarkdownHTML(std::string uriPath)
+{
+ return m_Markdowns[uriPath];
+}
diff --git a/src/content/markdown/Markdown.hpp b/src/content/markdown/Markdown.hpp
index 41c8b4d..9c4d3c4 100644
--- a/src/content/markdown/Markdown.hpp
+++ b/src/content/markdown/Markdown.hpp
@@ -4,10 +4,12 @@
#include "../../Content.hpp"
#include "../../Filesystem.hpp"
+using Markdowns = std::unordered_map<std::string, std::shared_ptr<std::string> >;
+
class Markdown : public Content
{
public:
- explicit Markdown(std::string uriBasePath, std::string markdownFilesPath);
+ explicit Markdown(std::string uriBasePath, std::string markdownFilesPath, std::string mainTemplatePath = "");
bool Init();
void Shutdown();
@@ -17,12 +19,17 @@ public:
std::string const & GetMainTemplate() const;
Redirections const & GetRedirections() const;
+ bool HasMarkdownFile(std::string filePath) const;
+ bool HasMarkdownURI(std::string uriPath) const;
+ Markdowns const & GetMarkdowns() const;
+ std::shared_ptr<std::string> const & GetMarkdownHTML(std::string uriPath);
+
private:
std::string m_UriBasePath;
std::string m_MainTemplatePath;
std::string m_MarkdownFilesPath;
Redirections m_Redirections;
- Filesystem m_MarkdownFiles;
+ Markdowns m_Markdowns;
};
#endif
diff --git a/src/content/static/Static.cpp b/src/content/static/Static.cpp
index b9977a9..132f0a0 100644
--- a/src/content/static/Static.cpp
+++ b/src/content/static/Static.cpp
@@ -6,7 +6,6 @@ Static::Static(std::string uriBasePath, std::shared_ptr<Filesystem> const & fs)
for (auto const & file : fs->GetFiles())
{
m_Redirections.push_back(uriBasePath + "/" + file.first);
- m_UriToFsMapping[uriBasePath + "/" + file.first] = file.first;
}
}
@@ -26,16 +25,21 @@ bool Static::Render(RequestResponse & rr, RenderData & rd, std::string & out)
{
(void)rd;
+ if (rr.GetUriPath() == m_UriBasePath)
+ {
+ return false;
+ }
+
rr.UseOutputHeader();
auto & files = m_StaticFiles->GetFiles();
- auto const & path = std::string(rr.GetUriPath());
+ auto const & path = std::string(rr.GetUriPath()).substr(m_UriBasePath.length() + 1, std::string::npos);
- if (rr.AddOutputHeader("Content-Type", files[m_UriToFsMapping[path]].mime) == false)
+ if (rr.AddOutputHeader("Content-Type", files[path].mime) == false)
{
return false;
}
- out = std::string(files[m_UriToFsMapping[path]].data.begin(), files[m_UriToFsMapping[path]].data.end());
+ out = std::string(files[path].data.begin(), files[path].data.end());
return true;
}
diff --git a/src/content/static/Static.hpp b/src/content/static/Static.hpp
index 8dcf410..b9de983 100644
--- a/src/content/static/Static.hpp
+++ b/src/content/static/Static.hpp
@@ -22,7 +22,6 @@ private:
std::string m_MainTemplatePath;
Redirections m_Redirections;
std::shared_ptr<Filesystem> const m_StaticFiles;
- std::unordered_map<std::string, std::string> m_UriToFsMapping;
};
#endif