diff options
Diffstat (limited to 'src/content')
-rw-r--r-- | src/content/blog/Blog.cpp | 138 | ||||
-rw-r--r-- | src/content/blog/Blog.hpp | 28 | ||||
-rw-r--r-- | src/content/markdown/Markdown.cpp | 47 | ||||
-rw-r--r-- | src/content/markdown/Markdown.hpp | 11 | ||||
-rw-r--r-- | src/content/static/Static.cpp | 12 | ||||
-rw-r--r-- | src/content/static/Static.hpp | 1 |
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 |