/*
* Copyright (C) 2022 luca0N!
*
* This file is part of Static Website Generator (swg).
*
* Static Website Generator (swg) is free software: you can redistribute it
* and/or modify it under the terms of the version 3 of the GNU Lesser General
* Public License as published by the Free Software Foundation.
*
* Static Website Generator (swg) is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Static Website Generator (swg). If not, see
* .
*
* Contact luca0N! by e-mail: .
*/
#include "BlogBuilder.hxx"
#include
#include
#include
#include "Article.hxx"
#include "WebsiteBuilder.hxx"
std::string blog_relative_path(std::string const &pathPrefix, std::string const &path) {
return path.substr(pathPrefix.length() - 1);
}
bool is_valid_article(std::string const &pathPrefix, std::string const &path) {
return std::regex_search(blog_relative_path(pathPrefix, path), std::regex("^/\\d{4}/\\d{2}/.*\\.md"));
}
void build_blog_structure(std::string const &path, std::string const &prefix, std::list const &articles, Blog *blog) {
std::filesystem::path obp = get_output_path(path); // Output Blog Path
obp /= "blog";
obp /= blog->dir;
try {
// Create blog directory
if (!std::filesystem::exists(obp)) std::filesystem::create_directories(obp);
} catch (std::filesystem::filesystem_error const &e) {
std::cerr << "error: failed to create directory for blog \"" << blog->name << "\": " << e.what() << std::endl;
exit(RETURN_FAILED_UNKNOWN_ERROR);
}
std::list am;
//std::map> sorted_articles;
for (std::string const &a : articles) {
std::string articlePath = blog_relative_path(prefix, a);
std::regex yearMonth("(\\d+)");
auto match = std::sregex_iterator(articlePath.begin(), articlePath.end(), yearMonth);
std::string year = match->str(),
month = (++match)->str();
// Go ahead and parse article metadata.
Article::Metadata *articleMetadata = (Article::Metadata*) malloc(sizeof(Article::Metadata));
Article::get_metadata(a, articleMetadata);
std::cout << "Parsed metadata for article \"" << articleMetadata->title << "\"\n\tPublished on "
<< ctime(&(articleMetadata->publish_ts)) << "\n";
// TODO: This code could be optimized by removing directory
// checks for every single article. Instead, add
// directory-checks for years and months to a queue (skipping
// existing ones) and then checking and creating the
// directories later as needed.
try {
// Create directory for the year of this article if it doesn't exist.
std::filesystem::path oad = obp, // Output Article Directory
rap; // Relative Article Path
oad /= year;
rap /= year;
if (!std::filesystem::exists(oad)) std::filesystem::create_directory(oad);
// Do the same for the article month.
oad /= month;
rap /= month;
if (!std::filesystem::exists(oad)) std::filesystem::create_directory(oad);
// Now create the article file.
std::string new_article_filename = getFilename(a, false);
new_article_filename += ".html";
oad /= new_article_filename;
rap /= new_article_filename;
compile_markdown(path, a, oad, articleMetadata);
strncpy(articleMetadata->path, rap.c_str(), sizeof(articleMetadata->path));
am.push_back(articleMetadata);
} catch (std::filesystem::filesystem_error const &e) {
std::cerr << "error: failed to create directory for an article from blog \""
<< blog->name << "\": " << e.what() << std::endl;
exit(RETURN_FAILED_UNKNOWN_ERROR);
}
}
// Sort am list.
am.sort(Article::Comparator::comp);
// Generate blog catalog.
std::string blog_html_catalog,
last_date = "";
blog_html_catalog += "";
blog_html_catalog += blog->name;
blog_html_catalog += " - Catalog
\n";
for (Article::Metadata *m : am) {
struct tm *article_time = gmtime(&(m->publish_ts));
std::string hr_date = get_hr_month(article_time->tm_mon);
hr_date += " ";
hr_date += std::to_string(article_time->tm_year + 1900);
// Check if this article belongs to the same "month + year" group as
// the last article. If it doesn't, add a new group to the catalog.
if (hr_date != last_date) {
last_date = hr_date;
blog_html_catalog += "\n\t";
blog_html_catalog += hr_date;
blog_html_catalog += "\n";
}
blog_html_catalog += "\t- path;
blog_html_catalog += "\">";
blog_html_catalog += m->title;
blog_html_catalog += "
\n";
// Free memory as it's no longer needed.
free(m);
}
blog_html_catalog += "
";
// Put catalog HTML into template and save it to the blog directory.
std::filesystem::path catalog_output_path = obp;
catalog_output_path /= "index.html";
std::string html_template = get_template(path);
std::regex content_placeholder("");
std::string catalog_html_contents = std::regex_replace(html_template, content_placeholder, blog_html_catalog);
FILE *catalog_output_file = fopen(catalog_output_path.c_str(), "w");
fputs(catalog_html_contents.c_str(), catalog_output_file);
fclose(catalog_output_file);
}