diff --git a/src/libs/extensionsystem/pluginmanager.cpp b/src/libs/extensionsystem/pluginmanager.cpp index 876bd31e80b..e4a278ec9bc 100644 --- a/src/libs/extensionsystem/pluginmanager.cpp +++ b/src/libs/extensionsystem/pluginmanager.cpp @@ -57,6 +57,8 @@ Q_LOGGING_CATEGORY(pluginLog, "qtc.extensionsystem", QtWarningMsg) const char C_IGNORED_PLUGINS[] = "Plugins/Ignored"; const char C_FORCEENABLED_PLUGINS[] = "Plugins/ForceEnabled"; +const char C_TANDCACCEPTED_PLUGINS[] = "Plugins/TermsAndConditionsAccepted"; + const std::chrono::milliseconds DELAYED_INITIALIZE_INTERVAL{20}; enum { debugLeaks = 0 }; @@ -1051,6 +1053,8 @@ void PluginManagerPrivate::readSettings() if (settings) { disabledPlugins = toLower(settings->value(C_IGNORED_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)); } +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 &callback) +{ + acceptTermsAndConditionsCallback = callback; +} + /*! \internal */ @@ -1700,6 +1732,13 @@ void PluginManagerPrivate::loadPlugin(PluginSpec *spec, PluginSpec::State destSt if (!spec->isEffectivelyEnabled() && destState == PluginSpec::Loaded) return; + if (spec->termsAndConditions()) { + if (!acceptTermsAndConditions(spec)) { + spec->setError(Tr::tr("You did not accept the terms and conditions")); + return; + } + } + std::unique_ptr lockFile; if (enableCrashCheck && destState < PluginSpec::Stopped) lockFile.reset(new LockFile(this, spec)); @@ -2026,4 +2065,10 @@ void PluginManager::startProfiling() d->m_profileElapsedMS = 0; } +void PluginManager::setAcceptTermsAndConditionsCallback( + const std::function &callback) +{ + d->setAcceptTermsAndConditionsCallback(callback); +} + } // ExtensionSystem diff --git a/src/libs/extensionsystem/pluginmanager.h b/src/libs/extensionsystem/pluginmanager.h index d601bf16180..6dee40b2a3d 100644 --- a/src/libs/extensionsystem/pluginmanager.h +++ b/src/libs/extensionsystem/pluginmanager.h @@ -137,6 +137,8 @@ public: static QString systemInformation(); + void setAcceptTermsAndConditionsCallback(const std::function &callback); + signals: void objectAdded(QObject *obj); void aboutToRemoveObject(QObject *obj); diff --git a/src/libs/extensionsystem/pluginmanager_p.h b/src/libs/extensionsystem/pluginmanager_p.h index 877a56c2e51..cdbd7ee52e1 100644 --- a/src/libs/extensionsystem/pluginmanager_p.h +++ b/src/libs/extensionsystem/pluginmanager_p.h @@ -68,6 +68,9 @@ public: void readSettings(); void writeSettings(); + bool acceptTermsAndConditions(PluginSpec *spec); + void setAcceptTermsAndConditionsCallback(const std::function &callback); + class TestSpec { public: TestSpec(PluginSpec *pluginSpec, const QStringList &testFunctionsOrObjects = QStringList()) @@ -98,6 +101,7 @@ public: QStringList defaultEnabledPlugins; // Plugins/ForceEnabled from install settings QStringList disabledPlugins; QStringList forceEnabledPlugins; + QStringList pluginsWithAcceptedTermsAndConditions; // delayed initialization QTimer delayedInitializeTimer; std::queue delayedInitializeQueue; @@ -115,6 +119,8 @@ public: Utils::QtcSettings *settings = nullptr; Utils::QtcSettings *globalSettings = nullptr; + std::function acceptTermsAndConditionsCallback; + // Look in argument descriptions of the specs for the option. PluginSpec *pluginForOption(const QString &option, bool *requiresArgument) const; PluginSpec *pluginById(const QString &id) const; diff --git a/src/libs/extensionsystem/pluginspec.cpp b/src/libs/extensionsystem/pluginspec.cpp index 426fb392f6c..c5622786cc2 100644 --- a/src/libs/extensionsystem/pluginspec.cpp +++ b/src/libs/extensionsystem/pluginspec.cpp @@ -202,6 +202,7 @@ public: QString copyright; QStringList arguments; QRegularExpression platformSpecification; + std::optional termsAndConditions; QVector dependencies; PluginSpec::PluginArgumentDescriptions argumentDescriptions; @@ -392,6 +393,11 @@ QRegularExpression PluginSpec::platformSpecification() const return d->platformSpecification; } +std::optional PluginSpec::termsAndConditions() const +{ + return d->termsAndConditions; +} + /*! Returns whether the plugin works on the host platform. */ @@ -734,6 +740,7 @@ namespace { const char ARGUMENT_NAME[] = "Name"; const char ARGUMENT_PARAMETER[] = "Parameter"; const char ARGUMENT_DESCRIPTION[] = "Description"; + const char TERMSANDCONDITIONS[] = "TermsAndConditions"; } /*! @@ -978,6 +985,23 @@ Utils::expected_str PluginSpecPrivate::readMetaData(const QJsonObject &dat if (auto r = assignOr(revision, "Revision", QString{}); !r.has_value()) 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)); if (!value.isUndefined() && !value.isString()) return reportError(msgValueIsNotAString(PLATFORM)); diff --git a/src/libs/extensionsystem/pluginspec.h b/src/libs/extensionsystem/pluginspec.h index c133f260092..b8d972f9ce0 100644 --- a/src/libs/extensionsystem/pluginspec.h +++ b/src/libs/extensionsystem/pluginspec.h @@ -34,6 +34,12 @@ class PluginSpecPrivate; class PluginView; +struct EXTENSIONSYSTEM_EXPORT TermsAndConditions +{ + int version; + QString text; +}; + struct EXTENSIONSYSTEM_EXPORT PluginDependency { enum Type { Required, Optional, Test }; @@ -110,6 +116,7 @@ public: virtual QString category() const; virtual QString revision() const; virtual QRegularExpression platformSpecification() const; + virtual std::optional termsAndConditions() const; virtual QString displayName() const; diff --git a/src/plugins/coreplugin/coreplugin.cpp b/src/plugins/coreplugin/coreplugin.cpp index 0777bd20422..16c8dfe9102 100644 --- a/src/plugins/coreplugin/coreplugin.cpp +++ b/src/plugins/coreplugin/coreplugin.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -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) { // register all mime types from all plugins @@ -171,7 +203,7 @@ bool CorePlugin::initialize(const QStringList &arguments, QString *errorMessage) continue; loadMimeFromPlugin(plugin); } - + initTAndCAcceptDialog(); initProxyAuthDialog(); if (ThemeEntry::availableThemes().isEmpty()) {