summaryrefslogtreecommitdiffhomepage
path: root/plugins
diff options
context:
space:
mode:
authorRoland Reichwein <mail@reichwein.it>2020-04-28 19:00:17 +0200
committerRoland Reichwein <mail@reichwein.it>2020-04-28 19:00:17 +0200
commit095c82585f2e4a9963c33c0988d453b0278bc0c7 (patch)
treeaf345d356a84278f4df9cf7b999871a9bfba4bd8 /plugins
parent37da545bc5f1bd5c3d810699cc685c36cdcd27fa (diff)
Paged list
Diffstat (limited to 'plugins')
-rw-r--r--plugins/weblog/Makefile1
-rw-r--r--plugins/weblog/stringutil.cpp66
-rw-r--r--plugins/weblog/stringutil.h10
-rw-r--r--plugins/weblog/weblog.cpp101
4 files changed, 163 insertions, 15 deletions
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 <boost/algorithm/string/predicate.hpp>
+#include <boost/algorithm/string/case_conv.hpp>
+
+#include <cstdarg>
+
+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<std::string> split(std::string value, const std::string separators)
+{
+ std::vector<std::string> 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<std::string> 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<std::string> &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 <string>
+#include <vector>
+
+std::string strfmt(const char* fmt, ...);
+
+std::vector<std::string> split(std::string value, const std::string separators = "\r\n ");
+std::string join(std::vector<std::string> vs, std::string separator = "\n");
+bool startsWithAnyOfLower(const std::string &s, const std::vector<std::string> &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 <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <boost/property_tree/ptree.hpp>
@@ -152,7 +154,7 @@ namespace {
}
}
- std::vector<ArticleInfo> getArticleList(fs::path& path)
+ std::vector<ArticleInfo> getArticleList(fs::path& path, size_t page)
{
std::vector<ArticleInfo> 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<std::string(const std::string& key)>& GetRequestParam,
- std::function<plugin_interface_setter_type>& SetResponseHeader)
+ std::function<plugin_interface_setter_type>& SetResponseHeader,
+ size_t page)
{
try {
HtmlPage htmlPage{GetRequestParam, "<h1>"s + GetRequestParam("WEBLOG_NAME") + "</h1>"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{"<a href=\"" + (link / article.path.filename()).string() + "/\">"};
- std::string linkend{"</a>"};
- htmlPage += "<h2>"s + linkstart + article.subject + linkend + "</h2>"s + article.date + "<br/>"s;
-
- auto sv{shortVersion(article.path)};
- if (sv.size()) {
- htmlPage += sv + " "s + linkstart + "more..." + linkend;
+ else {
+ for (const auto& article: list) {
+ std::string linkstart{"<a href=\"" + (link / article.path.filename()).string() + "/\">"};
+ std::string linkend{"</a>"};
+ htmlPage += "<h2>"s + linkstart + article.subject + linkend + "</h2>"s + article.date + "<br/>"s;
+
+ auto sv{shortVersion(article.path)};
+ if (sv.size()) {
+ htmlPage += sv + " "s + linkstart + "more..." + linkend;
+ }
}
+ htmlPage += "<br/><br/><br/>";
+ if (page > 0)
+ htmlPage += "<a href=\"?page="s + std::to_string(page - 1) + "\">&lt;&lt;newer</a> "s;
+ htmlPage += "page "s + std::to_string(page + 1);
+ if (list.size() == number_of_articles_on_front_page)
+ htmlPage += " <a href=\"?page="s + std::to_string(page + 1) + "\">older&gt;&gt;</a>"s;
+ htmlPage += "<br/>";
}
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<char>(i);
+ } catch (...) {
+ return result;
+ }
+
+ pos += 2;
+ } else {
+ result += c;
+ }
+ pos++;
+ }
+
+ return result;
+ }
+
+ std::unordered_map<std::string, std::string> SplitQueryString(std::string& s)
+ {
+ std::unordered_map<std::string, std::string> 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<std::string, std::string> 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);