ExtensionSystem: Add Terms & Conditions

Initial implementation of asking the user to accept Terms & Conditions
on a per-plugin basis.

Change-Id: Idb252efbdc5a2318bf059e35420fa7333b66880a
Reviewed-by: Alessandro Portale <alessandro.portale@qt.io>
This commit is contained in:
Marcus Tillmanns
2024-09-18 14:04:52 +02:00
parent 1c39164d89
commit a12139bc9c
6 changed files with 117 additions and 1 deletions

View File

@@ -57,6 +57,8 @@ Q_LOGGING_CATEGORY(pluginLog, "qtc.extensionsystem", QtWarningMsg)
const char C_IGNORED_PLUGINS[] = "Plugins/Ignored"; const char C_IGNORED_PLUGINS[] = "Plugins/Ignored";
const char C_FORCEENABLED_PLUGINS[] = "Plugins/ForceEnabled"; const char C_FORCEENABLED_PLUGINS[] = "Plugins/ForceEnabled";
const char C_TANDCACCEPTED_PLUGINS[] = "Plugins/TermsAndConditionsAccepted";
const std::chrono::milliseconds DELAYED_INITIALIZE_INTERVAL{20}; const std::chrono::milliseconds DELAYED_INITIALIZE_INTERVAL{20};
enum { debugLeaks = 0 }; enum { debugLeaks = 0 };
@@ -1051,6 +1053,8 @@ void PluginManagerPrivate::readSettings()
if (settings) { if (settings) {
disabledPlugins = toLower(settings->value(C_IGNORED_PLUGINS).toStringList()); disabledPlugins = toLower(settings->value(C_IGNORED_PLUGINS).toStringList());
forceEnabledPlugins = toLower(settings->value(C_FORCEENABLED_PLUGINS).toStringList()); forceEnabledPlugins = toLower(settings->value(C_FORCEENABLED_PLUGINS).toStringList());
pluginsWithAcceptedTermsAndConditions
= settings->value(C_TANDCACCEPTED_PLUGINS).toStringList();
} }
} }
@@ -1688,6 +1692,34 @@ PluginSpec *PluginManager::specForPlugin(IPlugin *plugin)
return findOrDefault(d->pluginSpecs, equal(&PluginSpec::plugin, plugin)); return findOrDefault(d->pluginSpecs, equal(&PluginSpec::plugin, plugin));
} }
bool PluginManagerPrivate::acceptTermsAndConditions(PluginSpec *spec)
{
if (pluginsWithAcceptedTermsAndConditions.contains(spec->id()))
return true;
if (!acceptTermsAndConditionsCallback) {
spec->setError(Tr::tr("No callback set to accept terms and conditions"));
return false;
}
if (!acceptTermsAndConditionsCallback(spec)) {
spec->setError(Tr::tr("You did not accept the terms and conditions"));
return false;
}
pluginsWithAcceptedTermsAndConditions.append(spec->id());
if (settings)
settings->setValue(C_TANDCACCEPTED_PLUGINS, pluginsWithAcceptedTermsAndConditions);
return true;
}
void PluginManagerPrivate::setAcceptTermsAndConditionsCallback(
const std::function<bool(PluginSpec *)> &callback)
{
acceptTermsAndConditionsCallback = callback;
}
/*! /*!
\internal \internal
*/ */
@@ -1700,6 +1732,13 @@ void PluginManagerPrivate::loadPlugin(PluginSpec *spec, PluginSpec::State destSt
if (!spec->isEffectivelyEnabled() && destState == PluginSpec::Loaded) if (!spec->isEffectivelyEnabled() && destState == PluginSpec::Loaded)
return; return;
if (spec->termsAndConditions()) {
if (!acceptTermsAndConditions(spec)) {
spec->setError(Tr::tr("You did not accept the terms and conditions"));
return;
}
}
std::unique_ptr<LockFile> lockFile; std::unique_ptr<LockFile> lockFile;
if (enableCrashCheck && destState < PluginSpec::Stopped) if (enableCrashCheck && destState < PluginSpec::Stopped)
lockFile.reset(new LockFile(this, spec)); lockFile.reset(new LockFile(this, spec));
@@ -2026,4 +2065,10 @@ void PluginManager::startProfiling()
d->m_profileElapsedMS = 0; d->m_profileElapsedMS = 0;
} }
void PluginManager::setAcceptTermsAndConditionsCallback(
const std::function<bool(PluginSpec *)> &callback)
{
d->setAcceptTermsAndConditionsCallback(callback);
}
} // ExtensionSystem } // ExtensionSystem

View File

@@ -137,6 +137,8 @@ public:
static QString systemInformation(); static QString systemInformation();
void setAcceptTermsAndConditionsCallback(const std::function<bool(PluginSpec *)> &callback);
signals: signals:
void objectAdded(QObject *obj); void objectAdded(QObject *obj);
void aboutToRemoveObject(QObject *obj); void aboutToRemoveObject(QObject *obj);

View File

@@ -68,6 +68,9 @@ public:
void readSettings(); void readSettings();
void writeSettings(); void writeSettings();
bool acceptTermsAndConditions(PluginSpec *spec);
void setAcceptTermsAndConditionsCallback(const std::function<bool(PluginSpec *)> &callback);
class TestSpec { class TestSpec {
public: public:
TestSpec(PluginSpec *pluginSpec, const QStringList &testFunctionsOrObjects = QStringList()) TestSpec(PluginSpec *pluginSpec, const QStringList &testFunctionsOrObjects = QStringList())
@@ -98,6 +101,7 @@ public:
QStringList defaultEnabledPlugins; // Plugins/ForceEnabled from install settings QStringList defaultEnabledPlugins; // Plugins/ForceEnabled from install settings
QStringList disabledPlugins; QStringList disabledPlugins;
QStringList forceEnabledPlugins; QStringList forceEnabledPlugins;
QStringList pluginsWithAcceptedTermsAndConditions;
// delayed initialization // delayed initialization
QTimer delayedInitializeTimer; QTimer delayedInitializeTimer;
std::queue<PluginSpec *> delayedInitializeQueue; std::queue<PluginSpec *> delayedInitializeQueue;
@@ -115,6 +119,8 @@ public:
Utils::QtcSettings *settings = nullptr; Utils::QtcSettings *settings = nullptr;
Utils::QtcSettings *globalSettings = nullptr; Utils::QtcSettings *globalSettings = nullptr;
std::function<bool(PluginSpec *)> acceptTermsAndConditionsCallback;
// Look in argument descriptions of the specs for the option. // Look in argument descriptions of the specs for the option.
PluginSpec *pluginForOption(const QString &option, bool *requiresArgument) const; PluginSpec *pluginForOption(const QString &option, bool *requiresArgument) const;
PluginSpec *pluginById(const QString &id) const; PluginSpec *pluginById(const QString &id) const;

View File

@@ -202,6 +202,7 @@ public:
QString copyright; QString copyright;
QStringList arguments; QStringList arguments;
QRegularExpression platformSpecification; QRegularExpression platformSpecification;
std::optional<TermsAndConditions> termsAndConditions;
QVector<ExtensionSystem::PluginDependency> dependencies; QVector<ExtensionSystem::PluginDependency> dependencies;
PluginSpec::PluginArgumentDescriptions argumentDescriptions; PluginSpec::PluginArgumentDescriptions argumentDescriptions;
@@ -392,6 +393,11 @@ QRegularExpression PluginSpec::platformSpecification() const
return d->platformSpecification; return d->platformSpecification;
} }
std::optional<TermsAndConditions> PluginSpec::termsAndConditions() const
{
return d->termsAndConditions;
}
/*! /*!
Returns whether the plugin works on the host platform. Returns whether the plugin works on the host platform.
*/ */
@@ -734,6 +740,7 @@ namespace {
const char ARGUMENT_NAME[] = "Name"; const char ARGUMENT_NAME[] = "Name";
const char ARGUMENT_PARAMETER[] = "Parameter"; const char ARGUMENT_PARAMETER[] = "Parameter";
const char ARGUMENT_DESCRIPTION[] = "Description"; const char ARGUMENT_DESCRIPTION[] = "Description";
const char TERMSANDCONDITIONS[] = "TermsAndConditions";
} }
/*! /*!
@@ -978,6 +985,23 @@ Utils::expected_str<void> PluginSpecPrivate::readMetaData(const QJsonObject &dat
if (auto r = assignOr(revision, "Revision", QString{}); !r.has_value()) if (auto r = assignOr(revision, "Revision", QString{}); !r.has_value())
return reportError(r.error()); return reportError(r.error());
QJsonObject tAndC = metaData.value(QLatin1String(TERMSANDCONDITIONS)).toObject();
if (!tAndC.isEmpty()) {
QJsonValue version = tAndC.value(QLatin1String("version"));
QJsonValue text = tAndC.value(QLatin1String("text"));
if (!version.isDouble()) {
return reportError(::ExtensionSystem::Tr::tr("Terms and conditions: %1")
.arg(msgValueMissing("version")));
}
if (!text.isString() || text.toString().isEmpty()) {
return reportError(
::ExtensionSystem::Tr::tr("Terms and conditions: %1").arg(msgValueMissing("text")));
}
termsAndConditions.emplace(TermsAndConditions{version.toInt(), text.toString()});
}
QJsonValue value = metaData.value(QLatin1String(PLATFORM)); QJsonValue value = metaData.value(QLatin1String(PLATFORM));
if (!value.isUndefined() && !value.isString()) if (!value.isUndefined() && !value.isString())
return reportError(msgValueIsNotAString(PLATFORM)); return reportError(msgValueIsNotAString(PLATFORM));

View File

@@ -34,6 +34,12 @@ class PluginSpecPrivate;
class PluginView; class PluginView;
struct EXTENSIONSYSTEM_EXPORT TermsAndConditions
{
int version;
QString text;
};
struct EXTENSIONSYSTEM_EXPORT PluginDependency struct EXTENSIONSYSTEM_EXPORT PluginDependency
{ {
enum Type { Required, Optional, Test }; enum Type { Required, Optional, Test };
@@ -110,6 +116,7 @@ public:
virtual QString category() const; virtual QString category() const;
virtual QString revision() const; virtual QString revision() const;
virtual QRegularExpression platformSpecification() const; virtual QRegularExpression platformSpecification() const;
virtual std::optional<TermsAndConditions> termsAndConditions() const;
virtual QString displayName() const; virtual QString displayName() const;

View File

@@ -31,6 +31,7 @@
#include <utils/checkablemessagebox.h> #include <utils/checkablemessagebox.h>
#include <utils/commandline.h> #include <utils/commandline.h>
#include <utils/infobar.h> #include <utils/infobar.h>
#include <utils/layoutbuilder.h>
#include <utils/macroexpander.h> #include <utils/macroexpander.h>
#include <utils/mimeutils.h> #include <utils/mimeutils.h>
#include <utils/networkaccessmanager.h> #include <utils/networkaccessmanager.h>
@@ -163,6 +164,37 @@ static void initProxyAuthDialog()
}); });
} }
static void initTAndCAcceptDialog()
{
ExtensionSystem::PluginManager::instance()->setAcceptTermsAndConditionsCallback(
[](ExtensionSystem::PluginSpec *spec) {
using namespace Layouting;
QDialog dialog(ICore::dialogParent());
dialog.setWindowTitle(Tr::tr("Terms and Conditions"));
QDialogButtonBox buttonBox(
QDialogButtonBox::StandardButton::Yes | QDialogButtonBox::StandardButton::No);
QObject::connect(&buttonBox, &QDialogButtonBox::accepted, &dialog, &QDialog::accept);
QObject::connect(&buttonBox, &QDialogButtonBox::rejected, &dialog, &QDialog::reject);
// clang-format off
Column {
Tr::tr("The plugin %1 requires you to accept the following terms and conditions:").arg(spec->name()), br,
TextEdit {
markdown(spec->termsAndConditions()->text),
readOnly(true),
}, br,
Row {
Tr::tr("Do you wish to accept?"), &buttonBox,
}
}.attachTo(&dialog);
// clang-format on
return dialog.exec() == QDialog::Accepted;
});
}
bool CorePlugin::initialize(const QStringList &arguments, QString *errorMessage) bool CorePlugin::initialize(const QStringList &arguments, QString *errorMessage)
{ {
// register all mime types from all plugins // register all mime types from all plugins
@@ -171,7 +203,7 @@ bool CorePlugin::initialize(const QStringList &arguments, QString *errorMessage)
continue; continue;
loadMimeFromPlugin(plugin); loadMimeFromPlugin(plugin);
} }
initTAndCAcceptDialog();
initProxyAuthDialog(); initProxyAuthDialog();
if (ThemeEntry::availableThemes().isEmpty()) { if (ThemeEntry::availableThemes().isEmpty()) {