From 095c82585f2e4a9963c33c0988d453b0278bc0c7 Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Tue, 28 Apr 2020 19:00:17 +0200 Subject: Paged list --- plugins/weblog/Makefile | 1 + plugins/weblog/stringutil.cpp | 66 +++++++++++++++++++++++++++ plugins/weblog/stringutil.h | 10 +++++ plugins/weblog/weblog.cpp | 101 +++++++++++++++++++++++++++++++++++------- 4 files changed, 163 insertions(+), 15 deletions(-) create mode 100644 plugins/weblog/stringutil.cpp create mode 100644 plugins/weblog/stringutil.h (limited to 'plugins/weblog') diff --git a/plugins/weblog/Makefile b/plugins/weblog/Makefile index ffd31e1..58d1801 100644 --- a/plugins/weblog/Makefile +++ b/plugins/weblog/Makefile @@ -57,6 +57,7 @@ LIBS+= \ endif PROGSRC=\ + stringutil.cpp \ weblog.cpp TESTSRC=\ diff --git a/plugins/weblog/stringutil.cpp b/plugins/weblog/stringutil.cpp new file mode 100644 index 0000000..f87fa00 --- /dev/null +++ b/plugins/weblog/stringutil.cpp @@ -0,0 +1,66 @@ +#include "stringutil.h" + +#include +#include + +#include + +std::string strfmt(const char* fmt, ...) +{ + va_list args; + + va_start(args, fmt); + int size = std::vsnprintf(nullptr, 0, fmt, args); + va_end(args); + + std::string result(size, ' '); + + va_start(args, fmt); + std::vsnprintf(result.data(), size + 1, fmt, args); + va_end(args); + + return result; +} + +std::vector split(std::string value, const std::string separators) +{ + std::vector result; + + size_t pos0 = 0; + size_t pos1 = 0; + while (pos0 < value.size()) { + pos1 = value.find_first_of(separators, pos0); + if (pos1 == std::string::npos) + pos1 = value.size(); + std::string part = value.substr(pos0, pos1 - pos0); + //std::cout << "DEBUG: " << part << std::endl << std::flush; + if (part != "") + result.push_back(part); + pos0 = value.find_first_not_of(separators, pos1); + if (pos0 == std::string::npos) + pos0 = value.size(); + } + + return result; +} + +std::string join(std::vector vs, std::string separator) +{ + std::string s; + for (const auto& line : vs) { + if (s.size() > 0) + s += separator; + s += line; + } + + return s; +} + +bool startsWithAnyOfLower(const std::string &s, const std::vector &list) { + for (const std::string& element : list) { + if (boost::algorithm::starts_with(boost::algorithm::to_lower_copy(s), boost::algorithm::to_lower_copy(element))) + return true; + } + return false; +} + diff --git a/plugins/weblog/stringutil.h b/plugins/weblog/stringutil.h new file mode 100644 index 0000000..5110e2e --- /dev/null +++ b/plugins/weblog/stringutil.h @@ -0,0 +1,10 @@ +#pragma once + +#include +#include + +std::string strfmt(const char* fmt, ...); + +std::vector split(std::string value, const std::string separators = "\r\n "); +std::string join(std::vector vs, std::string separator = "\n"); +bool startsWithAnyOfLower(const std::string &s, const std::vector &list); diff --git a/plugins/weblog/weblog.cpp b/plugins/weblog/weblog.cpp index 07b6447..a69fdb6 100644 --- a/plugins/weblog/weblog.cpp +++ b/plugins/weblog/weblog.cpp @@ -1,5 +1,7 @@ #include "weblog.h" +#include "stringutil.h" + #include #include #include @@ -152,7 +154,7 @@ namespace { } } - std::vector getArticleList(fs::path& path) + std::vector getArticleList(fs::path& path, size_t page) { std::vector result; @@ -166,11 +168,12 @@ namespace { } } - size_t size{std::min(number_of_articles_on_front_page, result.size())}; + size_t index0{std::min(number_of_articles_on_front_page * (page), result.size())}; + size_t index1{std::min(number_of_articles_on_front_page * (page + 1), result.size())}; // sort backwards - std::partial_sort(result.begin(), result.begin() + size, result.end(), [](const ArticleInfo& a0, const ArticleInfo& a1){ return a0.date > a1.date;}); + std::partial_sort(result.begin(), result.begin() + index1, result.end(), [](const ArticleInfo& a0, const ArticleInfo& a1){ return a0.date > a1.date;}); - return {result.begin(), result.begin() + size}; + return {result.begin() + index0, result.begin() + index1}; } std::string plainTextFromPTree(const pt::ptree& tree) @@ -271,25 +274,35 @@ namespace { std::string generateIndexPage(fs::path& path, std::function& GetRequestParam, - std::function& SetResponseHeader) + std::function& SetResponseHeader, + size_t page) { try { HtmlPage htmlPage{GetRequestParam, "

"s + GetRequestParam("WEBLOG_NAME") + "

"s}; fs::path link{ GetRequestParam("rel_target")}; - auto list{getArticleList(path)}; + auto list{getArticleList(path, page)}; if (list.empty()) htmlPage += "(no articles found.)"; - for (const auto& article: list) { - std::string linkstart{""}; - std::string linkend{""}; - htmlPage += "

"s + linkstart + article.subject + linkend + "

"s + article.date + "
"s; - - auto sv{shortVersion(article.path)}; - if (sv.size()) { - htmlPage += sv + " "s + linkstart + "more..." + linkend; + else { + for (const auto& article: list) { + std::string linkstart{""}; + std::string linkend{""}; + htmlPage += "

"s + linkstart + article.subject + linkend + "

"s + article.date + "
"s; + + auto sv{shortVersion(article.path)}; + if (sv.size()) { + htmlPage += sv + " "s + linkstart + "more..." + linkend; + } } + htmlPage += "


"; + if (page > 0) + htmlPage += "<<newer "s; + htmlPage += "page "s + std::to_string(page + 1); + if (list.size() == number_of_articles_on_front_page) + htmlPage += " older>>"s; + htmlPage += "
"; } return htmlPage; } catch (const std::exception& ex) { @@ -334,6 +347,56 @@ namespace { } } + std::string urlDecode(std::string s) + { + std::string result; + + size_t pos = 0; + while (pos < s.size()) { + char c {s[pos]}; + if (c == '+') { + result += ' '; + } else if (c == '%' && pos + 2 < s.size()) { + try { + int i = stoi(s.substr(pos + 1, 2), 0, 16); + if (i < 0 || i > 255) + return result; + + result += static_cast(i); + } catch (...) { + return result; + } + + pos += 2; + } else { + result += c; + } + pos++; + } + + return result; + } + + std::unordered_map SplitQueryString(std::string& s) + { + std::unordered_map result; + + size_t qpos = s.find('?'); + if (qpos != s.npos) { + auto list {split(s.substr(qpos + 1), "&")}; + for (auto i: list) { + size_t apos = i.find('='); + if (apos != i.npos) { + result[urlDecode(i.substr(0, apos))] = urlDecode(i.substr(apos + 1)); + } + } + } + + s = s.substr(0, qpos); + + return result; + } + } // anonymous namespace std::string weblog_plugin::name() @@ -370,6 +433,8 @@ std::string weblog_plugin::generate_page( return HttpStatus("400", "Illegal request: "s + target, SetResponseHeader); } + std::unordered_map query { SplitQueryString(rel_target) }; + // Build the path to the requested file std::string doc_root{GetRequestParam("doc_root")}; if (rel_target.size() >= 4 && std::all_of(rel_target.begin(), rel_target.begin() + 4, isdigit)) { @@ -384,8 +449,14 @@ std::string weblog_plugin::generate_page( SetResponseHeader("content_type", "text/html"); + size_t page {0}; + auto it {query.find("page")}; + if (it != query.end()) { + page = stoul(it->second); + } + if (is_index_page(rel_target)) - return generateIndexPage(path, GetRequestParam, SetResponseHeader); + return generateIndexPage(path, GetRequestParam, SetResponseHeader, page); if (is_article_page(rel_target, path)) return generateArticlePage(path, GetRequestParam, SetResponseHeader); -- cgit v1.2.3