article.cpp (5273B)
1 #include <format> 2 #include <iostream> 3 #include <iterator> 4 #include <numeric> 5 #include <optional> 6 #include <string> 7 8 #include "article.hpp" 9 10 #include <hemplate/attribute.hpp> 11 #include <hemplate/classes.hpp> 12 13 #include "utility.hpp" 14 15 namespace stamd { 16 17 std::optional<std::string> Article::get(const std::string& key) const 18 { 19 const auto itr = m_symbols.find(key); 20 if (itr == end(m_symbols)) 21 { 22 // std::cerr << "Warning: getting invalid value for: " << key << std::endl; 23 return {}; 24 } 25 return itr->second; 26 } 27 28 std::string Article::get_filename() const 29 { 30 return m_filename; 31 } 32 33 std::string Article::get_date() const 34 { 35 return get("date").value_or("0000-00-00"); 36 } 37 38 std::string Article::get_title() const 39 { 40 return get("title").value_or(get_filename()); 41 } 42 43 std::string Article::get_language() const 44 { 45 return get("language").value_or("en"); 46 } 47 48 std::string Article::get_desciprtion() const 49 { 50 return get("description").value_or(m_options.description); 51 } 52 53 std::string Article::get_author() const 54 { 55 return get("author").value_or(m_options.author); 56 } 57 58 std::string Article::get_keywords() const 59 { 60 static const auto concat = [](const categories_t& categories) 61 { 62 if (categories.empty()) return std::string(); 63 return std::accumulate(std::next(std::begin(categories)), 64 std::end(categories), 65 *categories.begin(), 66 [](const auto& acc, const auto& str) 67 { return acc + ", " + str; }); 68 }; 69 70 return get("keywords").value_or(concat(m_categories)); 71 } 72 73 void Article::print_nav(std::ostream& ost, const std::string& base) 74 { 75 using namespace hemplate; // NOLINT 76 77 ost << html::nav() 78 .add(html::a("<-- back", {{"class", "back"}})) 79 .add(html::a("index", {{"href", base}})) 80 .add(html::a("home -->", {{"href", "/"}})); 81 } 82 83 void Article::print_categories(std::ostream& ost, 84 const categories_t& categories) 85 { 86 using namespace hemplate; // NOLINT 87 88 ost << html::nav().set("class", "categories"); 89 ost << html::h3("Categories: "); 90 ost << html::p(); 91 for (const auto& category : categories) 92 { 93 auto ctgry = category; 94 normalize(ctgry); 95 ost << html::a(category, {{"href", std::format("./{}.html", ctgry)}}); 96 } 97 ost << html::p(); 98 ost << html::nav(); 99 } 100 101 void Article::write_header(std::ostream& ost) const 102 { 103 using namespace hemplate; // NOLINT 104 105 ost << html::doctype(); 106 ost << html::html().set("lang", get_language()); 107 ost << html::head() 108 .add(html::title(get_title())) 109 // Meta tags 110 .add(html::meta({{"charset", "UTF-8"}})) 111 .add(html::meta({{"name", "author"}, {"content", get_author()}})) 112 .add(html::meta( 113 {{"name", "description"}, {"content", get_desciprtion()}})) 114 .add(html::meta( 115 {{"name", "keywords"}, {"content", get_keywords()}})) 116 .add(html::meta( 117 {{"content", "width=device-width, initial-scale=1"}, 118 {"name", "viewport"}})) 119 // Stylesheets 120 .add(html::link({{"rel", "stylesheet"}, {"type", "text/css"}}) 121 .set("href", "/css/index.css")) 122 .add(html::link({{"rel", "stylesheet"}, {"type", "text/css"}}) 123 .set("href", "/css/colors.css")) 124 // Rss feed 125 .add(html::link({{"rel", "alternate"}, 126 {"type", "application/atom+xml"}, 127 {"title", "RSS feed"}, 128 {"href", "/blog/rss.xml"}})) 129 // Atom feed 130 .add(html::link({{"rel", "alternate"}, 131 {"type", "application/atom+xml"}, 132 {"title", "Atom feed"}, 133 {"href", "/blog/atom.xml"}})) 134 // Icons 135 .add(html::link({{"rel", "icon"}, {"type", "image/png"}}) 136 .set("sizes", "32x32") 137 .set("href", "/img/favicon-32x32.png")) 138 .add(html::link({{"rel", "icon"}, {"type", "image/png"}}) 139 .set("sizes", "16x16") 140 .set("href", "/img/favicon-16x16.png")); 141 142 ost << html::body(); 143 ost << html::input() 144 .set("type", "checkbox") 145 .set("id", "theme_switch") 146 .set("class", "theme_switch"); 147 148 ost << html::div().set("id", "content"); 149 150 if (!m_nonav) 151 { 152 ost << html::header(); 153 print_nav(ost, m_options.base_url + "blog"); 154 ost << html::hr(); 155 ost << html::header(); 156 } 157 158 ost << html::main(); 159 ost << html::label(" ") 160 .set("for", "theme_switch") 161 .set("class", "switch_label"); 162 163 if (!m_categories.empty()) print_categories(ost, m_categories); 164 } 165 166 void Article::write_footer(std::ostream& ost) const 167 { 168 using namespace hemplate; // NOLINT 169 170 ost << html::main(); 171 172 if (!m_nonav) 173 { 174 ost << html::footer(); 175 ost << html::hr(); 176 print_nav(ost, m_options.base_url + "blog"); 177 ost << html::footer(); 178 } 179 180 ost << html::div(); 181 ost << html::script(" ").set("src", "/scripts/main.js"); 182 ost << html::body(); 183 ost << html::html(); 184 } 185 186 } // namespace stamd