diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5f8845ccc..eaf61af1d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -164,6 +164,24 @@ else()
endif()
endif()
+# Load git commit hash.
+if(REVISION_FROM_GIT AND EXISTS "${CMAKE_SOURCE_DIR}/.git")
+ execute_process(COMMAND "git" "rev-parse" "--short" "HEAD"
+ WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
+ OUTPUT_VARIABLE APP_REVISION
+ ERROR_QUIET
+ OUTPUT_STRIP_TRAILING_WHITESPACE
+ )
+
+ message(STATUS "Detected git revision: '${APP_REVISION}'.")
+else()
+ set(APP_REVISION "")
+endif()
+
+if(NOT USE_WEBENGINE)
+ set(APP_REVISION "${APP_REVISION}-nowebengine")
+endif()
+
# Pass common defines.
add_compile_definitions(
APP_NAME="${APP_NAME}"
diff --git a/resources/desktop/com.github.rssguard.appdata.xml b/resources/desktop/com.github.rssguard.appdata.xml
index 6584b8a54..f46333df8 100644
--- a/resources/desktop/com.github.rssguard.appdata.xml
+++ b/resources/desktop/com.github.rssguard.appdata.xml
@@ -26,7 +26,7 @@
https://github.com/sponsors/martinrotter
-
+
none
diff --git a/resources/docs/Documentation.md b/resources/docs/Documentation.md
index fe5a1bc41..1db5761ee 100644
--- a/resources/docs/Documentation.md
+++ b/resources/docs/Documentation.md
@@ -17,6 +17,7 @@ There is a [Discord server](https://discord.gg/7xbVMPPNqH) for user communicatio
- [Built-in Web Browser with AdBlock](#webb)
- [Minor Features](#mife)
- [Files Downloader](#downl)
+ - [Node.js](#node)
- [Labels](#lbls)
- [Skins](#skin)
- [GUI Tweaking](#guit)
@@ -426,18 +427,13 @@ If you're not sure which version to use, **use the WebEngine-based RSS Guard**.
#### AdBlock
[Web-based variant](#webb) of RSS Guard offers ad-blocking functionality via [Adblocker](https://github.com/cliqz-oss/adblocker). Adblocker offers similar performance to [uBlock Origin](https://github.com/gorhill/uBlock).
-If you want to enable AdBlock in RSS Guard you need to do this:
-
-1. Have [Node.js](https://nodejs.org) with [NPM](https://www.npmjs.com) (which is usually included in Node.js installer) installed. Also you need to have paths `node.exe` and `npm` added to your system `PATH` environment available.
-2. The implementation requires additional [npm](https://www.npmjs.com) modules to be installed. You see the list of needed modules near the top of [this](https://github.com/martinrotter/rssguard/blob/master/resources/scripts/adblock/adblock-server.js) file.
-
-I understand that the above installation is not trivial, but it is necessary evil to have up-to-date and modern implementation of AdBlock in RSS Guard. Previous, `C++`-based, implementation was buggy, slow, and hard to maintain.
+If you want to use AdBlock, you need to have [Node.js](#node) installed.
You can find elaborate lists of AdBlock rules [here](https://easylist.to). You can just copy direct hyperlinks to those lists and paste them into the "Filter lists" text-box as shown below. Remember to always separate individual links with newlines. Same applies to "Custom filters", where you can insert individual filters, for example [filter](https://adblockplus.org/filter-cheatsheet) "idnes" to block all URLs with "idnes" in them.
-The way ad-blocking internally works is that RSS Guard starts local HTTP browser which provides ad-blocking API, which is subsequently called by RSS Guard. There is some caching done in between, which speeds up some ad-blocking decisions.
+The way ad-blocking internally works is that RSS Guard starts local `HTTP` browser which provides ad-blocking API, which is subsequently called by RSS Guard. There is some caching done in between, which speeds up some ad-blocking decisions.
## Minor Features
@@ -452,6 +448,12 @@ You can right click on any item in embedded web browser and hit `Save as` button
You can download up to 6 files simultaneously.
+### Node.js
+RSS Guard integrates [`Node.js`](https://nodejs.org). Go to `Node.js` section of `Settings` dialog to see more.
+
+`Node.js` is used for some advanced functionality like [AdBlock](#adbl).
+
+
### Labels
RSS Guard supports labels (tags). Any number of tags can be assigned to any article.
diff --git a/resources/scripts/adblock/adblock-server.js b/resources/scripts/adblock/adblock-server.js
index 6b27ff13d..368ebbd38 100644
--- a/resources/scripts/adblock/adblock-server.js
+++ b/resources/scripts/adblock/adblock-server.js
@@ -1,11 +1,10 @@
// Simple local HTTP server providing ad-blocking functionality via https://github.com/cliqz-oss/adblocker
//
// How to install:
-// npm i -g tldts-experimental
-// npm i -g @cliqz/adblocker
+// npm i @cliqz/adblocker
//
// How to run:
-// NODE_PATH="C:\Users\\AppData\Roaming\npm\node_modules" node ./adblock-server.js "" ""
+// NODE_PATH="..." node ./adblock-server.js "" ""
//
// How to use:
// curl -i -X POST --data '
diff --git a/src/librssguard/CMakeLists.txt b/src/librssguard/CMakeLists.txt
index ea4ccaf6f..eb58d70cd 100644
--- a/src/librssguard/CMakeLists.txt
+++ b/src/librssguard/CMakeLists.txt
@@ -514,24 +514,6 @@ set(GMAIL_CLIENT_SECRET "" CACHE STRING "GMail client secret")
set(INOREADER_CLIENT_ID "" CACHE STRING "Inoreader client ID")
set(INOREADER_CLIENT_SECRET "" CACHE STRING "Inoreader client secret")
-# Load git commit hash.
-if(REVISION_FROM_GIT AND EXISTS "${CMAKE_SOURCE_DIR}/.git")
- execute_process(COMMAND "git" "rev-parse" "--short" "HEAD"
- WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
- OUTPUT_VARIABLE APP_REVISION
- ERROR_QUIET
- OUTPUT_STRIP_TRAILING_WHITESPACE
- )
-
- message(STATUS "Detected git revision: '${APP_REVISION}'.")
-else()
- set(APP_REVISION "")
-endif()
-
-if(NOT USE_WEBENGINE)
- set(APP_REVISION "${APP_REVISION}-nowebengine")
-endif()
-
# Bundle icons on some platforms which do not provide system-wide icon themes.
if(APPLE OR WIN32 OR OS2)
qt_add_resources(SOURCES ${CMAKE_SOURCE_DIR}/resources/icons.qrc)
diff --git a/src/librssguard/core/feeddownloader.cpp b/src/librssguard/core/feeddownloader.cpp
index 0f753f0ab..383afa7e3 100644
--- a/src/librssguard/core/feeddownloader.cpp
+++ b/src/librssguard/core/feeddownloader.cpp
@@ -368,7 +368,7 @@ void FeedDownloader::updateOneFeed(ServiceRoot* acc,
<< feed->customId() << " stored in DB.";
if (updated_messages.first > 0) {
- m_results.appendUpdatedFeed(QPair(feed->title(), updated_messages.first));
+ m_results.appendUpdatedFeed({ feed->title(), updated_messages.first });
}
}
catch (const FeedFetchException& feed_ex) {
@@ -412,8 +412,7 @@ void FeedDownloader::finalizeUpdate() {
emit updateFinished(m_results);
}
-bool FeedDownloader::isCacheSynchronizationRunning() const
-{
+bool FeedDownloader::isCacheSynchronizationRunning() const {
return m_isCacheSynchronizationRunning;
}
diff --git a/src/librssguard/definitions/definitions.h b/src/librssguard/definitions/definitions.h
index 180e4d79d..1aa656654 100644
--- a/src/librssguard/definitions/definitions.h
+++ b/src/librssguard/definitions/definitions.h
@@ -16,7 +16,7 @@
#define SERVICE_CODE_REDDIT "reddit"
#define ADBLOCK_SERVER_PORT 48484
-#define ADBLOCK_HOWTO "https://github.com/martinrotter/rssguard/blob/master/resources/docs/Documentation.md#adblock"
+#define ADBLOCK_HOWTO "https://github.com/martinrotter/rssguard/blob/master/resources/docs/Documentation.md#adbl"
#define ADBLOCK_ICON_ACTIVE "adblock"
#define ADBLOCK_ICON_DISABLED "adblock-disabled"
diff --git a/src/librssguard/gui/settings/settingsgeneral.cpp b/src/librssguard/gui/settings/settingsgeneral.cpp
index 565177207..cf6feffed 100644
--- a/src/librssguard/gui/settings/settingsgeneral.cpp
+++ b/src/librssguard/gui/settings/settingsgeneral.cpp
@@ -9,6 +9,7 @@ SettingsGeneral::SettingsGeneral(Settings* settings, QWidget* parent)
: SettingsPanel(settings, parent), m_ui(new Ui::SettingsGeneral) {
m_ui->setupUi(this);
m_ui->m_checkAutostart->setText(m_ui->m_checkAutostart->text().arg(QSL(APP_NAME)));
+ m_ui->m_checkForUpdatesOnStart->setText(m_ui->m_checkForUpdatesOnStart->text().arg(QSL(APP_NAME)));
connect(m_ui->m_checkAutostart, &QCheckBox::stateChanged, this, &SettingsGeneral::dirtifySettings);
connect(m_ui->m_checkForUpdatesOnStart, &QCheckBox::stateChanged, this, &SettingsGeneral::dirtifySettings);
diff --git a/src/librssguard/gui/settings/settingsgeneral.ui b/src/librssguard/gui/settings/settingsgeneral.ui
index ee8570772..e8f740113 100644
--- a/src/librssguard/gui/settings/settingsgeneral.ui
+++ b/src/librssguard/gui/settings/settingsgeneral.ui
@@ -21,7 +21,7 @@
-
- Check for updates on application startup
+ Check for %1 updates on application startup
diff --git a/src/librssguard/miscellaneous/application.cpp b/src/librssguard/miscellaneous/application.cpp
index bbfdcd0e6..13f21e281 100644
--- a/src/librssguard/miscellaneous/application.cpp
+++ b/src/librssguard/miscellaneous/application.cpp
@@ -60,6 +60,7 @@ Application::Application(const QString& id, int& argc, char** argv)
m_mainForm = nullptr;
m_trayIcon = nullptr;
m_settings = Settings::setupSettings(this);
+ m_nodejs = new NodeJs(m_settings, this);
m_webFactory = new WebFactory(this);
m_system = new SystemFactory(this);
m_skins = new SkinFactory(this);
@@ -68,7 +69,6 @@ Application::Application(const QString& id, int& argc, char** argv)
m_database = new DatabaseFactory(this);
m_downloadManager = nullptr;
m_notifications = new NotificationFactory(this);
- m_nodejs = new NodeJs(m_settings, this);
m_shouldRestart = false;
determineFirstRuns();
@@ -663,13 +663,9 @@ void Application::downloadRequested(QWebEngineDownloadItem* download_item) {
void Application::onAdBlockFailure() {
qApp->showGuiMessage(Notification::Event::GeneralEvent, {
tr("AdBlock needs to be configured"),
- tr("AdBlock component is not configured properly."),
- QSystemTrayIcon::MessageIcon::Critical },
- {}, {
- tr("Configure now"),
- [=]() {
- m_webFactory->adBlock()->showDialog();
- } });
+ tr("AdBlock component is not configured properly. Go to \"Settings\" -> \"Node.js\" and check "
+ "if your Node.js is properly configured."),
+ QSystemTrayIcon::MessageIcon::Critical }, {});
qApp->settings()->setValue(GROUP(AdBlock), AdBlock::AdBlockEnabled, false);
}
diff --git a/src/librssguard/miscellaneous/nodejs.cpp b/src/librssguard/miscellaneous/nodejs.cpp
index a7155a1b3..c863fdfc1 100644
--- a/src/librssguard/miscellaneous/nodejs.cpp
+++ b/src/librssguard/miscellaneous/nodejs.cpp
@@ -15,6 +15,16 @@
NodeJs::NodeJs(Settings* settings, QObject* parent) : QObject(parent), m_settings(settings) {}
+void NodeJs::runScript(QProcess* proc, const QString& script, const QStringList& arguments) const {
+ QStringList arg = { script }; arg.append(arguments);
+ QProcessEnvironment env;
+ QString node_modules = processedPackageFolder() + QDir::separator() + QSL("node_modules");
+
+ env.insert(QSL("NODE_PATH"), node_modules);
+
+ IOFactory::startProcess(proc, nodeJsExecutable(), arg, env);
+}
+
QString NodeJs::nodeJsExecutable() const {
return QDir::toNativeSeparators(m_settings->value(GROUP(Node), SETTING(Node::NodeJsExecutable)).toString());
}
@@ -94,6 +104,8 @@ void NodeJs::installUpdatePackage(const PackageMetadata& pkg) {
break;
case PackageStatus::UpToDate:
+ qDebugNN << LOGSEC_NODEJS << "Package" << QUOTE_W_SPACE(pkg.m_name) << "is up-to-date.";
+
emit packageInstalledUpdated(pkg);
break;
@@ -101,8 +113,6 @@ void NodeJs::installUpdatePackage(const PackageMetadata& pkg) {
}
void NodeJs::installPackage(const PackageMetadata& pkg) {
- // npm install --prefix "." @cliqz/adblocker@">=1.0.0 <2.0.0" --production --save-exact
- //https://docs.npmjs.com/cli/v8/commands/npm-install
try {
QProcess* proc = new QProcess();
@@ -132,6 +142,8 @@ void NodeJs::installPackage(const PackageMetadata& pkg) {
emit packageError(pkg, sndr->errorString());
});
+ qDebugNN << LOGSEC_NODEJS << "Installing package" << QUOTE_W_SPACE_DOT(pkg.m_name);
+
IOFactory::startProcess(proc,
npmExecutable(),
{ QSL("install"), QSL("--production"),
@@ -139,11 +151,9 @@ void NodeJs::installPackage(const PackageMetadata& pkg) {
QSL("--prefix"), processedPackageFolder() });
}
catch (const ProcessException& ex) {
+ qCriticalNN << LOGSEC_NODEJS << "Package" << QUOTE_W_SPACE(pkg.m_name)
+ "was not installed, error:" << QUOTE_W_SPACE_DOT(ex.message());
+
emit packageError(pkg, ex.message());
}
}
-
-void NodeJs::updatePackage(const PackageMetadata& pkg) {
- // npm update --prefix "." @cliqz/adblocker@">=1.0.0 <2.0.0" --production --save-exact
- //https://docs.npmjs.com/cli/v8/commands/npm-update
-}
diff --git a/src/librssguard/miscellaneous/nodejs.h b/src/librssguard/miscellaneous/nodejs.h
index 326e4d702..b565644be 100644
--- a/src/librssguard/miscellaneous/nodejs.h
+++ b/src/librssguard/miscellaneous/nodejs.h
@@ -6,35 +6,38 @@
#include
class Settings;
+class QProcess;
class NodeJs : public QObject {
Q_OBJECT
- struct PackageMetadata {
- public:
-
- // Name of package.
- QString m_name;
-
- // Version description. This could be fixed version or empty
- // string (latest version) or perhaps version range.
- QString m_version;
- };
-
- enum class PackageStatus {
- // Package not installed.
- NotInstalled,
-
- // Package installed but out-of-date.
- OutOfDate,
-
- // Package installed and up-to-date.
- UpToDate
- };
-
public:
+ struct PackageMetadata {
+ public:
+
+ // Name of package.
+ QString m_name;
+
+ // Version description. This could be fixed version or empty
+ // string (latest version) or perhaps version range.
+ QString m_version;
+ };
+
+ enum class PackageStatus {
+ // Package not installed.
+ NotInstalled,
+
+ // Package installed but out-of-date.
+ OutOfDate,
+
+ // Package installed and up-to-date.
+ UpToDate
+ };
+
explicit NodeJs(Settings* settings, QObject* parent = nullptr);
+ void runScript(QProcess* proc, const QString& script, const QStringList& arguments) const;
+
QString nodeJsExecutable() const;
void setNodeJsExecutable(const QString& exe) const;
@@ -67,7 +70,6 @@ class NodeJs : public QObject {
private:
void installPackage(const PackageMetadata& pkg);
- void updatePackage(const PackageMetadata& pkg);
Settings* m_settings;
};
diff --git a/src/librssguard/network-web/adblock/adblockdialog.cpp b/src/librssguard/network-web/adblock/adblockdialog.cpp
index 994dd9c8e..6d6e0acbe 100644
--- a/src/librssguard/network-web/adblock/adblockdialog.cpp
+++ b/src/librssguard/network-web/adblock/adblockdialog.cpp
@@ -112,7 +112,7 @@ void AdBlockDialog::onAdBlockProcessTerminated() {
m_ui.m_cbEnable->setChecked(false);
m_ui.m_lblTestResult->setStatus(WidgetWithStatus::StatusType::Error,
tr("There is error, check application log for more details and "
- "head to online documentation. Also make sure that Node.js is installed."),
+ "head to online documentation."),
tr("ERROR!"));
}
diff --git a/src/librssguard/network-web/adblock/adblockmanager.cpp b/src/librssguard/network-web/adblock/adblockmanager.cpp
index ef3aea4c9..050b0b88f 100644
--- a/src/librssguard/network-web/adblock/adblockmanager.cpp
+++ b/src/librssguard/network-web/adblock/adblockmanager.cpp
@@ -25,11 +25,14 @@
#include
AdBlockManager::AdBlockManager(QObject* parent)
- : QObject(parent), m_loaded(false), m_enabled(false), m_interceptor(new AdBlockUrlInterceptor(this)),
+ : QObject(parent), m_loaded(false), m_enabled(false), m_installing(false), m_interceptor(new AdBlockUrlInterceptor(this)),
m_serverProcess(nullptr), m_cacheBlocks({}) {
m_adblockIcon = new AdBlockIcon(this);
m_adblockIcon->setObjectName(QSL("m_adblockIconAction"));
m_unifiedFiltersFile = qApp->userDataFolder() + QDir::separator() + QSL("adblock-unified-filters.txt");
+
+ connect(qApp->nodejs(), &NodeJs::packageInstalledUpdated, this, &AdBlockManager::onPackageReady);
+ connect(qApp->nodejs(), &NodeJs::packageError, this, &AdBlockManager::onPackageError);
}
AdBlockManager::~AdBlockManager() {
@@ -98,16 +101,9 @@ void AdBlockManager::setEnabled(bool enabled) {
emit enabledChanged(m_enabled);
if (m_enabled) {
- try {
- updateUnifiedFiltersFileAndStartServer();
- }
- catch (const ApplicationException& ex) {
- qCriticalNN << LOGSEC_ADBLOCK
- << "Failed to write unified filters to file or re-start server, error:"
- << QUOTE_W_SPACE_DOT(ex.message());
-
- m_enabled = false;
- emit enabledChanged(m_enabled);
+ if (!m_installing) {
+ m_installing = true;
+ qApp->nodejs()->installUpdatePackage({ QSL(CLIQZ_ADBLOCKED_PACKAGE), QSL(CLIQZ_ADBLOCKED_VERSION) });
}
}
else {
@@ -179,6 +175,37 @@ void AdBlockManager::showDialog() {
AdBlockDialog(qApp->mainFormWidget()).exec();
}
+void AdBlockManager::onPackageReady(const NodeJs::PackageMetadata& pkg) {
+ if (pkg.m_name == QSL(CLIQZ_ADBLOCKED_PACKAGE)) {
+ m_installing = false;
+
+ if (m_enabled) {
+ try {
+ updateUnifiedFiltersFileAndStartServer();
+ }
+ catch (const ApplicationException& ex) {
+ qCriticalNN << LOGSEC_ADBLOCK
+ << "Failed to setup filters and start server:"
+ << QUOTE_W_SPACE_DOT(ex.message());
+
+ m_enabled = false;
+ emit enabledChanged(m_enabled);
+ }
+ }
+ }
+}
+
+void AdBlockManager::onPackageError(const NodeJs::PackageMetadata& pkg, const QString& error) {
+ if (pkg.m_name == QSL(CLIQZ_ADBLOCKED_PACKAGE)) {
+ m_installing = false;
+ m_enabled = false;
+
+ qCriticalNN << LOGSEC_ADBLOCK << "Needed Node.js packages were not installed:" << QUOTE_W_SPACE_DOT(error);
+
+ emit processTerminated();
+ }
+}
+
void AdBlockManager::onServerProcessFinished(int exit_code, QProcess::ExitStatus exit_status) {
Q_UNUSED(exit_status)
killServer();
@@ -281,48 +308,54 @@ QProcess* AdBlockManager::startServer(int port) {
QProcess* proc = new QProcess(this);
-#if defined(Q_OS_WIN)
- proc->setProgram(QSL("node.exe"));
-#else
- proc->setProgram(QSL("node"));
-#endif
+ proc->setProcessChannelMode(QProcess::ProcessChannelMode::ForwardedErrorChannel);
- proc->setArguments({
- QDir::toNativeSeparators(temp_server),
+ connect(proc, QOverload::of(&QProcess::finished), this, &AdBlockManager::onServerProcessFinished);
+
+ qApp->nodejs()->runScript(proc, QDir::toNativeSeparators(temp_server), {
QString::number(port),
QDir::toNativeSeparators(m_unifiedFiltersFile)
});
- proc->setProcessEnvironment(QProcessEnvironment::systemEnvironment());
+ /*
+ #if defined(Q_OS_WIN)
+ proc->setProgram(QSL("node.exe"));
+ #else
+ proc->setProgram(QSL("node"));
+ #endif
- auto pe = proc->processEnvironment();
+ proc->setArguments({
+ QDir::toNativeSeparators(temp_server),
+ QString::number(port),
+ QDir::toNativeSeparators(m_unifiedFiltersFile)
+ });
- if (!pe.contains(QSL("NODE_PATH"))) {
- try {
+ proc->setProcessEnvironment(QProcessEnvironment::systemEnvironment());
+
+ auto pe = proc->processEnvironment();
+
+ if (!pe.contains(QSL("NODE_PATH"))) {
+ try {
const QString system_node_prefix = IOFactory::startProcessGetOutput(
-#if defined(Q_OS_WIN)
+ #if defined(Q_OS_WIN)
QSL("npm.cmd")
-#else
+ #else
QSL("npm")
-#endif
+ #endif
, { QSL("root"), QSL("--quiet"), QSL("-g") }
);
if (!system_node_prefix.isEmpty()) {
pe.insert(QSL("NODE_PATH"), system_node_prefix.simplified());
}
- }
- catch (const ApplicationException& ex) {
+ }
+ catch (const ApplicationException& ex) {
qWarningNN << LOGSEC_ADBLOCK << "Failed to get NPM root path:" << QUOTE_W_SPACE_DOT(ex.message());
- }
- }
+ }
+ }
- proc->setProcessEnvironment(pe);
- proc->setProcessChannelMode(QProcess::ProcessChannelMode::ForwardedErrorChannel);
-
- connect(proc, QOverload::of(&QProcess::finished), this, &AdBlockManager::onServerProcessFinished);
-
- proc->open();
+ proc->setProcessEnvironment(pe);
+ */
qDebugNN << LOGSEC_ADBLOCK << "Attempting to start AdBlock server.";
return proc;
diff --git a/src/librssguard/network-web/adblock/adblockmanager.h b/src/librssguard/network-web/adblock/adblockmanager.h
index 11600aca9..77b5c37e7 100644
--- a/src/librssguard/network-web/adblock/adblockmanager.h
+++ b/src/librssguard/network-web/adblock/adblockmanager.h
@@ -5,9 +5,14 @@
#include
+#include "miscellaneous/nodejs.h"
+
#include
#include
+#define CLIQZ_ADBLOCKED_PACKAGE "@cliqz/adblocker"
+#define CLIQZ_ADBLOCKED_VERSION "1.23.5"
+
class QUrl;
class AdblockRequestInfo;
class AdBlockUrlInterceptor;
@@ -66,6 +71,8 @@ class AdBlockManager : public QObject {
void processTerminated();
private slots:
+ void onPackageReady(const NodeJs::PackageMetadata& pkg);
+ void onPackageError(const NodeJs::PackageMetadata& pkg, const QString& error);
void onServerProcessFinished(int exit_code, QProcess::ExitStatus exit_status);
private:
@@ -79,6 +86,7 @@ class AdBlockManager : public QObject {
private:
bool m_loaded;
bool m_enabled;
+ bool m_installing;
AdBlockIcon* m_adblockIcon;
AdBlockUrlInterceptor* m_interceptor;
QString m_unifiedFiltersFile;