index.cpp (5485B)
1 #include <algorithm> 2 #include <chrono> 3 #include <format> 4 #include <fstream> 5 #include <numeric> 6 #include <sstream> 7 8 #include "index.hpp" 9 10 #include <hemplate/attribute.hpp> 11 #include <hemplate/classes.hpp> 12 13 namespace stamd { 14 15 std::tm get_time(const std::string& date) 16 { 17 int year = 0; 18 int month = 0; 19 int day = 0; 20 21 std::sscanf(date.c_str(), "%d-%d-%d", &year, &month, &day); 22 23 tm time = { 24 .tm_sec = 0, 25 .tm_min = 0, 26 .tm_hour = 0, 27 .tm_mday = day, 28 .tm_mon = month - 1, 29 .tm_year = year - 1900, 30 }; 31 32 return time; 33 } 34 35 #define rfc882_f "{:%a, %d %b %Y %H:%M:%S %z}" // NOLINT 36 #define rfc3339_f "{:%FT%H:%M:%SZ}" // NOLINT 37 38 std::string to_rfc882(const std::string& date) 39 { 40 using namespace std::chrono; // NOLINT 41 42 tm time = get_time(date); 43 44 const auto tmp = std::mktime(&time); 45 const auto chrono_time = 46 time_point_cast<seconds>(system_clock::from_time_t(tmp)); 47 48 return std::format(rfc882_f, chrono_time); 49 } 50 51 std::string to_rfc3339(const std::string& date) 52 { 53 using namespace std::chrono; // NOLINT 54 55 tm time = get_time(date); 56 57 const auto tmp = std::mktime(&time); 58 const auto chrono_time = 59 time_point_cast<seconds>(system_clock::from_time_t(tmp)); 60 61 return std::format(rfc3339_f, chrono_time); 62 } 63 64 void create_index(std::ostream& ost, 65 const std::string& name, 66 const article_list& articles, 67 const categories_t& categories) 68 { 69 using namespace hemplate; // NOLINT 70 71 const article index(name, categories); 72 73 index.write_header(ost); 74 ost << html::h1(name); 75 ost << html::ul().set("class", "index"); 76 for (const auto& article : articles) 77 { 78 if (article->is_hidden()) continue; 79 80 const auto& filename = article->get_filename(); 81 const auto& title = article->get_title(); 82 const auto& date = article->get_date(); 83 84 ost << html::li() 85 .add(html::span(std::format("{} - ", date))) 86 .add(html::a(title).set("href", filename)); 87 }; 88 ost << html::ul(); 89 index.write_footer(ost); 90 } 91 92 void create_atom(std::ostream& ost, 93 const std::string& name, 94 const article_list& articles) 95 { 96 using namespace hemplate; // NOLINT 97 98 static const char* base = "https://dimitrijedobrota.com/blog"; 99 static const char* loc = "https://dimitrijedobrota.com/blog/atom.xml"; 100 static const char* summary = "Click on the article link to read..."; 101 102 auto const time = 103 std::chrono::current_zone()->to_local(std::chrono::system_clock::now()); 104 105 ost << xml(); 106 ost << atom::feed(); 107 ost << atom::title(name); 108 ost << atom::id(base); 109 ost << atom::updated(std::format(rfc3339_f, time)); 110 ost << atom::author().add(atom::name(name)); 111 ost << atom::link(" ", {{"rel", "self"}, {"href", loc}}); 112 ost << atom::link( 113 " ", {{"href", base}, {"rel", "alternate"}, {"type", "text/html"}}); 114 115 for (const auto& article : articles) 116 { 117 const auto filename = article->get_filename(); 118 const auto title = article->get_title(); 119 const auto date = article->get_date(); 120 const auto path = std::format("{}/{}", base, filename); 121 122 ost << atom::entry() 123 .add(atom::title(title)) 124 .add(atom::id(path)) 125 .add(atom::link(" ").set("href", path)) 126 .add(atom::updated(to_rfc3339(date))) 127 .add(atom::summary(summary)); 128 } 129 130 ost << atom::feed(); 131 } 132 133 void create_rss(std::ostream& ost, 134 const std::string& name, 135 const article_list& articles) 136 { 137 using namespace hemplate; // NOLINT 138 139 static const char* author = "Dimitrije Dobrota"; 140 static const char* email = "mail@dimitrijedobrota.com"; 141 static const char* base = "https://dimitrijedobrota.com/blog"; 142 static const char* description = "Contents of Dimitrije Dobrota's webpage"; 143 static const char* loc = "https://dimitrijedobrota.com/blog/rss.xml"; 144 145 ost << xml(); 146 ost << rss::rss(); 147 ost << rss::channel(); 148 ost << rss::title(name); 149 ost << rss::link(base); 150 ost << rss::description(description); 151 ost << rss::generator("stamd"); 152 ost << rss::language("en-us"); 153 ost << rss::atomLink().set("href", loc); 154 155 for (const auto& article : articles) 156 { 157 const auto filename = article->get_filename(); 158 const auto date = article->get_date(); 159 160 ost << rss::item() 161 .add(rss::title(filename)) 162 .add(rss::link(std::format("{}/{}", base, filename))) 163 .add(rss::guid(std::format("{}/{}", base, filename))) 164 .add(rss::pubDate(to_rfc882(date))) 165 .add(rss::author(std::format("{} ({})", email, author))); 166 } 167 168 ost << rss::channel(); 169 ost << rss::rss(); 170 } 171 172 void create_sitemap(std::ostream& ost, const article_list& articles) 173 { 174 using namespace hemplate; // NOLINT 175 176 static const char* base = "https://dimitrijedobrota.com/blog"; 177 178 ost << xml(); 179 ost << sitemap::urlset(); 180 for (const auto& article : articles) 181 { 182 const auto& name = article->get_filename(); 183 const auto& date = article->get_date(); 184 185 ost << sitemap::url() 186 .add(sitemap::loc(std::format("{}/{}.html", base, name))) 187 .add(sitemap::lastmod(date)); 188 } 189 ost << sitemap::urlset(); 190 } 191 192 void create_robots(std::ostream& ost) 193 { 194 static const char* base = "https://dimitrijedobrota.com/blog"; 195 196 ost << "User-agent: *"; 197 ost << std::format("Sitemap: {}/sitemap.xml", base); 198 } 199 200 } // namespace stamd