diff --git a/.drone.yml b/.drone.yml index d5529b0..506fe55 100644 --- a/.drone.yml +++ b/.drone.yml @@ -26,7 +26,7 @@ steps: - alias apt-get='rm -f /var/cache/apt/archives/lock && apt-get' - apt-get update -q - apt-get install -qq build-essential cmake g++-10 clang pkg-config - - apt-get install -qq catch libcgicc-dev nlohmann-json3-dev libgit2-dev libcurl4-openssl-dev libicu-dev libfmt-dev + - apt-get install -qq catch libcgicc-dev nlohmann-json3-dev libgit2-dev libcurl4-openssl-dev libicu-dev libfmt-dev libpugixml-dev - rm -rf build && mkdir -p build && cd build - cmake -G "Unix Makefiles" -DWITH_TESTS=YES .. - make VERBOSE=1 @@ -62,7 +62,7 @@ steps: - alias apt-get='rm -f /var/cache/apt/archives/lock && apt-get' - apt-get update -q - apt-get install -qq build-essential cmake clang pkg-config - - apt-get install -qq catch libcgicc-dev nlohmann-json-dev libgit2-dev libcurl4-openssl-dev libicu-dev libfmt-dev + - apt-get install -qq catch libcgicc-dev nlohmann-json-dev libgit2-dev libcurl4-openssl-dev libicu-dev libfmt-dev libpugixml-dev - rm -rf build && mkdir -p build && cd build - cmake -G "Unix Makefiles" -DWITH_TESTS=YES .. - make VERBOSE=1 @@ -92,7 +92,7 @@ steps: - alias apt-get='rm -f /var/cache/apt/archives/lock && apt-get' - apt-get update -q - apt-get install -qq build-essential cmake clang pkg-config - - apt-get install -qq catch libcgicc-dev nlohmann-json-dev libgit2-dev libcurl4-gnutls-dev libicu-dev libfmt-dev + - apt-get install -qq catch libcgicc-dev nlohmann-json-dev libgit2-dev libcurl4-gnutls-dev libicu-dev libfmt-dev libpugixml-dev - rm -rf build && mkdir -p build && cd build - cmake -G "Unix Makefiles" -DWITH_TESTS=YES .. - make VERBOSE=1 diff --git a/CMakeLists.txt b/CMakeLists.txt index 0940ac3..1f92fae 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,6 +43,7 @@ pkg_check_modules(libgit2 REQUIRED IMPORTED_TARGET libgit2) find_package(CURL 7.56 REQUIRED) find_package(ICU REQUIRED COMPONENTS uc) find_package(fmt 4 REQUIRED CONFIG) +find_package(pugixml REQUIRED CONFIG) add_subdirectory(src) add_subdirectory(src/generators) diff --git a/CREDITS b/CREDITS index 804c9a4..72275ec 100644 --- a/CREDITS +++ b/CREDITS @@ -44,3 +44,8 @@ FediBlock-backend makes direct use of the following libraries and programs: From: Victor Zverovich, Jonathan Müller and community https://fmt.dev/ License: MIT + + pugixml + From: Arseny KapoulkineVictor and community + https://pugixml.org/ + License: MIT diff --git a/README.adoc b/README.adoc index 7650f1f..2c920e8 100644 --- a/README.adoc +++ b/README.adoc @@ -18,6 +18,7 @@ :uri-libcurl: https://curl.haxx.se/libcurl/ :uri-icu: http://site.icu-project.org/ :uri-fmt: https://github.com/fmtlib/fmt +:uri-pugixml: https://pugixml.org/ *{project}* turns form data into JSON and opens a pull request on link:https://schlomp.space/FediBlock/data[FediBlock/data]. Also included are @@ -115,6 +116,7 @@ The RSS generator expects the blocklist to be in * link:{uri-libcurl}[libcurl] (at least: 7.56) * link:{uri-icu}[ICU] (tested: 67.1 / 60.2) * link:{uri-fmt}[fmt] (tested: 7.0 / 4.0) +* link:{uri-pugixml}[pugixml] (tested: 1.10 / 1.8) * Optional ** Tests: link:{uri-catch}[Catch] (tested: 2.5 / 1.10) ** DEB package: link:{uri-dpkg}[dpkg] (tested: 1.19) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7fec9f5..7753476 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -20,7 +20,8 @@ target_link_libraries(fediblock std::filesystem PkgConfig::libgit2 ICU::uc - fmt::fmt) + fmt::fmt + pugixml) if(${CMAKE_VERSION} VERSION_LESS 3.12) target_link_libraries(fediblock PUBLIC ${CURL_LIBRARIES}) diff --git a/src/generators/rss.cpp b/src/generators/rss.cpp index 14d2576..75df842 100644 --- a/src/generators/rss.cpp +++ b/src/generators/rss.cpp @@ -21,6 +21,7 @@ #include "time.hpp" #include +#include #include #include @@ -47,27 +48,6 @@ using std::string_view; using std::uint8_t; using std::chrono::system_clock; -void write_line(ostream &out, const uint8_t spaces, const string_view rsstag, - const string_view value) -{ - // clang-format off - const string endtag{[&rsstag] - { - // If there is a space in the rsstag, use only the part up until the - // space for the ending RSS tag. - const size_t pos = rsstag.find(' '); - if (pos == string_view::npos) - { - return rsstag; - } - return rsstag.substr(0, pos); - }()}; - // clang-format off - - out << string(spaces, ' '); - out << '<' << rsstag << '>' << value << "\n"; -} - void write_rss(ostream &out, const vector &entries, const vector &tags) { @@ -95,38 +75,43 @@ void write_rss(ostream &out, const vector &entries, constexpr string_view rss_time_format{"%a, %d %b %Y %T %z"}; - out << R"( - -)"; - out << R"( )" << '\n'; - string title{"FediBlock: "}; - string description{"The newest FediBlock entries. "}; + pugi::xml_document doc; + auto rss{doc.append_child("rss")}; + rss.append_attribute("version") = "2.0"; + rss.append_attribute("xmlns:atom") = "http://www.w3.org/2005/Atom"; + + auto channel{rss.append_child("channel")}; + string tmp_title{"FediBlock: "}; + string tmp_description{"The newest FediBlock entries. "}; if (tags.empty()) { - title += "All tags"; - description += "All tags"; + tmp_title += "All tags"; + tmp_description += "All tags"; } else { - description += "Tags: "; + tmp_description += "Tags: "; } for (const auto &tag : tags) { if (tag != *tags.begin()) { - title += ", "; - description += ", "; + tmp_title += ", "; + tmp_description += ", "; } - title += tag; - description += tag; + tmp_title += tag; + tmp_description += tag; } - description += "."; - write_line(out, 4, "title", title); - write_line(out, 4, "link", baseurl); - write_line(out, 4, "description", description); - write_line(out, 4, "lastBuildDate", - time::to_string(system_clock::now(), rss_time_format)); + tmp_description += "."; + auto title{channel.append_child("title")}; + title.text() = tmp_title.c_str(); + auto link{channel.append_child("link")}; + link.text() = baseurl.c_str(); + auto description{channel.append_child("description")}; + description.text() = tmp_description.c_str(); + auto lastBuildDate{channel.append_child("lastBuildDate")}; + lastBuildDate.text() = time::to_string(system_clock::now(), rss_time_format) + .c_str(); for (const auto &entry : entries) { @@ -146,47 +131,53 @@ void write_rss(ostream &out, const vector &entries, // clang-format on } - out << " \n"; - write_line(out, 6, "title", entry.instance); - write_line(out, 6, "guid isPermaLink=\"false\"", - "FediBlock: " + entry.report_time + " " + entry.instance); - write_line(out, 6, "pubDate", - time::to_string(entry.report_time, rss_time_format)); - write_line(out, 6, "link", - format("{:s}#{:s}", baseurl, entry.instance)); + auto item{channel.append_child("item")}; + auto item_title{item.append_child("title")}; + item_title.text() = entry.instance.c_str(); + auto item_guid{item.append_child("guid")}; + item_guid.append_attribute("isPermaLink") = "false"; + item_guid.text() = format("FediBlock: {:s} {:s}", entry.report_time, + entry.instance) + .c_str(); + auto item_pubDate{item.append_child("pubDate")}; + item_pubDate.text() = time::to_string(entry.report_time, + rss_time_format) + .c_str(); + auto item_link{item.append_child("link")}; + item_link.text() = format("{:s}#{:s}", baseurl, entry.instance).c_str(); - string item_description{format("

{:s}

Tags: ", - cgi::text2html(entry.description))}; + string tmp_item_description{format("

{:s}

" + "

Tags: ", + cgi::text2html(entry.description))}; for (const auto &tag : entry.tags) { if (tag != *entry.tags.begin()) { - item_description += ", "; + tmp_item_description += ", "; } - item_description += tag; + tmp_item_description += tag; } - item_description += "

Receipts:
    "; + tmp_item_description += "

    Receipts:
      "; for (const auto &receipt : entry.receipts) { - item_description += - format("
    • {0:s}
    • ", receipt); + tmp_item_description += format( + "
    • {0:s}
    • ", receipt); } - item_description += "
    "; + tmp_item_description += "
"; if (!entry.screenshot_filepath.empty()) { - item_description += "

Screenshot:
"; - item_description += - format("

\n", - baseurl, entry.screenshot_filepath); + tmp_item_description += "

Screenshot:
"; + tmp_item_description += format( + "

\n", + baseurl, entry.screenshot_filepath); } - write_line(out, 6, "description", - format("", item_description)); - - out << "
\n"; + auto item_description{item.append_child("description")}; + item_description.append_child(pugi::node_cdata) + .set_value(tmp_item_description.c_str()); } - out << "
\n
\n"; + doc.print(out); } } // namespace FediBlock::rss