Added RSS export.
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
This commit is contained in:
parent
7557064215
commit
c9b82fd774
|
@ -5,8 +5,8 @@
|
|||
an URI to the archived version, the current date and time, title, description,
|
||||
the full text of the page and optional tags.
|
||||
|
||||
The database can be filtered by time, tags and full text and exported to CSV or
|
||||
AsciiDoc.
|
||||
The database can be filtered by time, tags and full text and exported to CSV,
|
||||
AsciiDoc, JSON or RSS.
|
||||
|
||||
Archiving is done using the Wayback machine from the
|
||||
https://archive.org/[Internet Archive].
|
||||
|
|
|
@ -2,7 +2,7 @@ include(CMakeFindDependencyMacro)
|
|||
include(GNUInstallDirs)
|
||||
|
||||
find_depencency(Poco
|
||||
COMPONENTS Foundation Net NetSSL Data DataSQLite JSON
|
||||
COMPONENTS Foundation Net NetSSL Data DataSQLite JSON XML
|
||||
CONFIG REQUIRED)
|
||||
find_dependency(PkgConfig REQUIRED)
|
||||
pkg_check_modules(libxdg-basedir REQUIRED IMPORTED_TARGET libxdg-basedir)
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
/* This file is part of remwharead.
|
||||
* Copyright © 2019 tastytea <tastytea@tastytea.de>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REMWHAREAD_RSS_HPP
|
||||
#define REMWHAREAD_RSS_HPP
|
||||
|
||||
#include <string>
|
||||
#include "export.hpp"
|
||||
|
||||
namespace remwharead
|
||||
{
|
||||
namespace Export
|
||||
{
|
||||
using std::string;
|
||||
|
||||
/*!
|
||||
* @brief Export as RSS feed.
|
||||
*
|
||||
* @since 0.8.0
|
||||
*
|
||||
* @headerfile rss.hpp remwharead/export/rss.hpp
|
||||
*/
|
||||
class RSS : protected ExportBase
|
||||
{
|
||||
public:
|
||||
using ExportBase::ExportBase;
|
||||
|
||||
virtual void print() const override;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif // REMWHAREAD_RSS_HPP
|
|
@ -45,6 +45,7 @@
|
|||
#include "export/export.hpp"
|
||||
#include "export/simple.hpp"
|
||||
#include "export/json.hpp"
|
||||
#include "export/rss.hpp"
|
||||
#include "search.hpp"
|
||||
#include "sqlite.hpp"
|
||||
#include "time.hpp"
|
||||
|
|
|
@ -35,7 +35,8 @@ namespace remwharead
|
|||
asciidoc,
|
||||
bookmarks,
|
||||
simple,
|
||||
json
|
||||
json,
|
||||
rss
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
:doctype: manpage
|
||||
:Author: tastytea
|
||||
:Email: tastytea@tastytea.de
|
||||
:Date: 2019-09-03
|
||||
:Date: 2019-09-06
|
||||
:Revision: 0.0.0
|
||||
:man source: remwharead
|
||||
:man manual: General Commands Manual
|
||||
|
@ -24,7 +24,7 @@ remwharead - Saves URIs of things you want to remember in a database
|
|||
the full text of the page and optional tags.
|
||||
|
||||
The database can be filtered by time, tags and full text and exported to CSV,
|
||||
AsciiDoc, a bookmarks file or JSON.
|
||||
AsciiDoc, a bookmarks file, JSON or RSS.
|
||||
|
||||
Archiving is done using the Wayback machine from the
|
||||
https://archive.org/[Internet Archive].
|
||||
|
@ -36,7 +36,7 @@ Add tags to _URI_, delimited by commas.
|
|||
|
||||
*-e* _format_, *--export* _format_::
|
||||
Export to _format_. Possible values are _csv_, _asciidoc_, _bookmarks_,
|
||||
_simple_ or _json_. See _FORMATS_.
|
||||
_simple_, _json_ or _rss_. See _FORMATS_.
|
||||
|
||||
*-f* _file_, *--file* _file_::
|
||||
Save output to _file_. Default is stdout.
|
||||
|
@ -139,13 +139,19 @@ Export as JSON array. See https://tools.ietf.org/html/rfc8259[RFC 8259]. Each
|
|||
object contains the members _uri_, _archive_uri_, _datetime_, _tags_ (array),
|
||||
_title_, _description_ and _fulltext_.
|
||||
|
||||
=== rss
|
||||
|
||||
Export as http://www.rssboard.org/rss-specification[RSS] feed. Because the URL
|
||||
of the feed is unknown to *remwharead*, the generated feed is slightly out of
|
||||
specification (the element _link_ in _channel_ is empty).
|
||||
|
||||
== SEARCH EXPRESSIONS
|
||||
|
||||
A search expression is either a single term, or several terms separated by _AND_
|
||||
or _OR_. _AND_ takes precedence. The expression _Mountain AND Big OR Vegetable_
|
||||
finds all things that have either Mountain and Big, or Vegetable in them. You can
|
||||
use _||_ instead of _OR_ and _&&_ instead of _AND_. Note that *--search-tags*
|
||||
only matches whole tags, Pill does not match Pillow.
|
||||
finds all things that have either Mountain and Big, or Vegetable in them. You
|
||||
can use _||_ instead of _OR_ and _&&_ instead of _AND_. Note that
|
||||
*--search-tags* only matches whole tags, Pill does not match Pillow.
|
||||
|
||||
== PROTOCOL SUPPORT
|
||||
|
||||
|
|
|
@ -10,4 +10,4 @@ Version: @PROJECT_VERSION@
|
|||
Cflags: -I${includedir}
|
||||
Libs: -L${libdir} -l${name} -lPocoData -lstdc++fs
|
||||
Requires.private: libxdg-basedir, icu-uc, icu-i18n
|
||||
Libs.private: -lPocoFoundation -lPocoNet -lPocoNetSSL -lPocoDataSQLite -lPocoJSON
|
||||
Libs.private: -lPocoFoundation -lPocoNet -lPocoNetSSL -lPocoDataSQLite -lPocoJSON -lPocoXML
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include "export/bookmarks.hpp"
|
||||
#include "export/simple.hpp"
|
||||
#include "export/json.hpp"
|
||||
#include "export/rss.hpp"
|
||||
#include "search.hpp"
|
||||
|
||||
using namespace remwharead;
|
||||
|
@ -191,6 +192,19 @@ int App::main(const std::vector<std::string> &args)
|
|||
}
|
||||
break;
|
||||
}
|
||||
case export_format::rss:
|
||||
{
|
||||
if (file.is_open())
|
||||
{
|
||||
Export::RSS(entries, file).print();
|
||||
file.close();
|
||||
}
|
||||
else
|
||||
{
|
||||
Export::RSS(entries).print();
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
break;
|
||||
|
|
|
@ -143,6 +143,10 @@ void App::handle_options(const std::string &name, const std::string &value)
|
|||
{
|
||||
_format = export_format::json;
|
||||
}
|
||||
else if (value == "rss")
|
||||
{
|
||||
_format = export_format::rss;
|
||||
}
|
||||
else
|
||||
{
|
||||
cerr << "Error: Unknown format.\n";
|
||||
|
|
|
@ -3,7 +3,9 @@ include(GNUInstallDirs)
|
|||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(libxdg-basedir REQUIRED IMPORTED_TARGET libxdg-basedir)
|
||||
# Some distributions do not contain Poco*Config.cmake recipes.
|
||||
find_package(Poco COMPONENTS Foundation Net NetSSL Data DataSQLite JSON CONFIG)
|
||||
find_package(Poco
|
||||
COMPONENTS Foundation Net NetSSL Data DataSQLite JSON XML
|
||||
CONFIG)
|
||||
|
||||
file(GLOB_RECURSE sources_lib *.cpp)
|
||||
file(GLOB_RECURSE headers_lib ../../include/*.hpp)
|
||||
|
@ -28,7 +30,8 @@ target_link_libraries(${PROJECT_NAME}
|
|||
# If no Poco*Config.cmake recipes are found, look for headers in standard dirs.
|
||||
if(PocoNetSSL_FOUND)
|
||||
target_link_libraries(${PROJECT_NAME}
|
||||
PRIVATE Poco::Foundation Poco::Net Poco::NetSSL Poco::DataSQLite Poco::JSON
|
||||
PRIVATE Poco::Foundation Poco::Net Poco::NetSSL Poco::DataSQLite
|
||||
Poco::JSON Poco::XML
|
||||
PUBLIC Poco::Data)
|
||||
else()
|
||||
find_file(Poco_h NAMES "Poco/Poco.h"
|
||||
|
@ -42,7 +45,7 @@ else()
|
|||
"but the files seem to be in the standard directories. "
|
||||
"Let's hope this works.")
|
||||
target_link_libraries(${PROJECT_NAME}
|
||||
PRIVATE PocoFoundation PocoNet PocoNetSSL PocoDataSQLite PocoJSON
|
||||
PRIVATE PocoFoundation PocoNet PocoNetSSL PocoDataSQLite PocoJSON PocoXML
|
||||
PUBLIC PocoData)
|
||||
endif()
|
||||
endif()
|
||||
|
|
|
@ -0,0 +1,139 @@
|
|||
/* This file is part of remwharead.
|
||||
* Copyright © 2019 tastytea <tastytea@tastytea.de>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <ctime>
|
||||
#include <Poco/XML/XMLWriter.h>
|
||||
#include <Poco/SAX/AttributesImpl.h>
|
||||
#include <Poco/DateTime.h>
|
||||
#include <Poco/DateTimeFormatter.h>
|
||||
#include <Poco/Timestamp.h>
|
||||
#include "time.hpp"
|
||||
#include "version.hpp"
|
||||
#include "export/rss.hpp"
|
||||
|
||||
namespace remwharead
|
||||
{
|
||||
using std::cerr;
|
||||
using std::endl;
|
||||
using std::time_t;
|
||||
using Poco::XML::XMLWriter;
|
||||
using Poco::XML::AttributesImpl;
|
||||
using Poco::DateTime;
|
||||
using Poco::DateTimeFormatter;
|
||||
using Poco::Timestamp;
|
||||
|
||||
void Export::RSS::print() const
|
||||
{
|
||||
try
|
||||
{
|
||||
XMLWriter writer(_out, XMLWriter::CANONICAL);
|
||||
AttributesImpl attrs_rss, attrs_guid;
|
||||
constexpr char timefmt_rfc822[] = "%w, %d %b %Y %H:%M:%S %Z";
|
||||
|
||||
attrs_rss.addAttribute("", "", "version", "", "2.0");
|
||||
attrs_rss.addAttribute("", "", "xmlns:atom", "",
|
||||
"http://www.w3.org/2005/Atom");
|
||||
attrs_guid.addAttribute("", "", "isPermaLink", "", "false");
|
||||
|
||||
writer.startDocument();
|
||||
writer.startElement("", "", "rss", attrs_rss);
|
||||
writer.startElement("", "", "channel");
|
||||
|
||||
writer.startElement("", "", "title");
|
||||
writer.characters("Visited things");
|
||||
writer.endElement("", "", "title");
|
||||
|
||||
writer.startElement("", "", "link");
|
||||
// FIXME: There has to be an URL here.
|
||||
writer.endElement("", "", "link");
|
||||
|
||||
writer.startElement("", "", "description");
|
||||
writer.characters("Export from remwharead.");
|
||||
writer.endElement("", "", "description");
|
||||
|
||||
writer.startElement("", "", "generator");
|
||||
writer.characters(string("remwharead ") + global::version);
|
||||
writer.endElement("", "", "generator");
|
||||
|
||||
const string now = DateTimeFormatter::format(DateTime(),
|
||||
timefmt_rfc822);
|
||||
writer.startElement("", "", "lastBuildDate");
|
||||
writer.characters(now);
|
||||
writer.endElement("", "", "lastBuildDate");
|
||||
|
||||
for (const Database::entry &entry : _entries)
|
||||
{
|
||||
writer.startElement("", "", "item");
|
||||
|
||||
writer.startElement("", "", "title");
|
||||
writer.characters(entry.title);
|
||||
writer.endElement("", "", "title");
|
||||
|
||||
writer.startElement("", "", "link");
|
||||
writer.characters(entry.uri);
|
||||
writer.endElement("", "", "link");
|
||||
|
||||
writer.startElement("", "", "guid", attrs_guid);
|
||||
writer.characters(entry.uri + " at " +
|
||||
timepoint_to_string(entry.datetime));
|
||||
writer.endElement("", "", "guid");
|
||||
|
||||
const time_t time = system_clock::to_time_t(entry.datetime);
|
||||
const string time_visited = DateTimeFormatter::format(
|
||||
Timestamp::fromEpochTime(time), timefmt_rfc822);
|
||||
writer.startElement("", "", "pubDate");
|
||||
writer.characters(time_visited);
|
||||
writer.endElement("", "", "pubDate");
|
||||
|
||||
string description = entry.description;
|
||||
if (!description.empty())
|
||||
{
|
||||
description += "\n\n";
|
||||
}
|
||||
if (!entry.tags.empty())
|
||||
{
|
||||
description += "Tags: ";
|
||||
for (const string &tag : entry.tags)
|
||||
{
|
||||
description += tag;
|
||||
if (tag != *(entry.tags.rbegin()))
|
||||
{
|
||||
description += ", ";
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!entry.archive_uri.empty())
|
||||
{
|
||||
description += "\n\nArchived version: " + entry.archive_uri;
|
||||
}
|
||||
writer.startElement("", "", "description");
|
||||
writer.characters(description);
|
||||
writer.endElement("", "", "description");
|
||||
|
||||
writer.endElement("", "", "item");
|
||||
}
|
||||
|
||||
writer.endElement("", "", "channel");
|
||||
writer.endElement("", "", "rss");
|
||||
writer.endDocument();
|
||||
_out << endl;
|
||||
}
|
||||
catch (std::exception &e)
|
||||
{
|
||||
cerr << "Error in " << __func__ << ": " << e.what() << endl;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue