/* * 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); }