Commit 4edc847d authored by Utkarsh Ayachit's avatar Utkarsh Ayachit

add support for plugins with EULA.

`add_paraview_plugin` now supports specifying EULA. EULA is
auto-accepted on command line executables or auto-load plugins.
User is prompted to accept it in Qt client.
parent 04156d08
......@@ -93,6 +93,7 @@ SET (TESTS_WITHOUT_BASELINES
${CMAKE_CURRENT_SOURCE_DIR}/FindWidget.xml
${CMAKE_CURRENT_SOURCE_DIR}/OpenHelp.xml
${CMAKE_CURRENT_SOURCE_DIR}/PartialArrayInLineChart.xml
${CMAKE_CURRENT_SOURCE_DIR}/PluginEULA.xml
${CMAKE_CURRENT_SOURCE_DIR}/Preview.xml
${CMAKE_CURRENT_SOURCE_DIR}/SearchBox.xml
${CMAKE_CURRENT_SOURCE_DIR}/ServerConnectDialog.xml
......
<?xml version="1.0" ?>
<pqevents>
<pqevent object="pqClientMainWindow/menubar" command="activate" arguments="menuTools" />
<pqevent object="pqClientMainWindow/menubar/menuTools" command="activate" arguments="actionManage_Plugins" />
<pqevent object="pqClientMainWindow/PluginManagerDialog/localGroup/localPlugins" command="setCurrent" arguments="AcceleratedAlgorithms" />
<pqevent object="pqClientMainWindow/PluginManagerDialog/localGroup/localPlugins" command="setCurrent" arguments="EULATestPlugin" />
<pqevent object="pqClientMainWindow/PluginManagerDialog/localGroup/loadSelected_Local" command="activate" arguments="" />
<pqevent object="pqClientMainWindow/PluginEULADialog/buttonBox/1QPushButton1" command="activate" arguments="" />
<pqevent object="pqClientMainWindow/PluginManagerDialog/localGroup/localPlugins" command="setCurrent" arguments="EULATestPlugin" />
<pqevent object="pqClientMainWindow/PluginManagerDialog/localGroup/loadSelected_Local" command="activate" arguments="" />
<pqevent object="pqClientMainWindow/PluginEULADialog/buttonBox/1QPushButton0" command="activate" arguments="" />
<pqevent object="pqClientMainWindow/PluginManagerDialog/localGroup/localPlugins" command="setCurrent" arguments="EULATestPlugin" />
<pqevent object="pqClientMainWindow/PluginManagerDialog/localGroup/loadSelected_Local" command="activate" arguments="" />
<pqevent object="pqClientMainWindow/PluginManagerDialog/buttonBox/1QPushButton0" command="activate" arguments="" />
</pqevents>
......@@ -719,6 +719,10 @@ ENDMACRO()
# html/css/png/jpg files that comprise of the documentation for the plugin. In
# addition, CMake will automatically generate documentation for any proxies
# defined in XMLs for this plugin.
# EULA (optional) :- a text file with the text for the EULA for the plugin.
# the user is required to accept the EULA before the plugin
# can be loaded.
#
# ADD_PARAVIEW_PLUGIN(Name Version
# [DOCUMENTATION_DIR dir]
# [SERVER_MANAGER_SOURCES source files]
......@@ -734,6 +738,7 @@ ENDMACRO()
# [REQUIRED_ON_CLIENT]
# [REQUIRED_PLUGINS pluginname1 pluginname2]
# [CS_KITS kit1 kit2...]
# [EULA eulafile]
# [EXCLUDE_FROM_DEFAULT_TARGET]
# )
FUNCTION(ADD_PARAVIEW_PLUGIN NAME VERSION)
......@@ -754,6 +759,7 @@ FUNCTION(ADD_PARAVIEW_PLUGIN NAME VERSION)
SET(ARG_AUTOLOAD)
SET(ARG_CS_KITS)
SET(ARG_DOCUMENTATION_DIR)
SET(ARG_EULA)
SET(PLUGIN_NAME "${NAME}")
SET(PLUGIN_VERSION "${VERSION}")
......@@ -778,7 +784,7 @@ FUNCTION(ADD_PARAVIEW_PLUGIN NAME VERSION)
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR})
PV_PLUGIN_PARSE_ARGUMENTS(ARG
"DOCUMENTATION_DIR;SERVER_MANAGER_SOURCES;SERVER_MANAGER_XML;SERVER_SOURCES;PYTHON_MODULES;GUI_INTERFACES;GUI_RESOURCES;GUI_RESOURCE_FILES;GUI_SOURCES;SOURCES;REQUIRED_PLUGINS;REQUIRED_ON_SERVER;REQUIRED_ON_CLIENT;EXCLUDE_FROM_DEFAULT_TARGET;AUTOLOAD;CS_KITS"
"DOCUMENTATION_DIR;SERVER_MANAGER_SOURCES;SERVER_MANAGER_XML;SERVER_SOURCES;PYTHON_MODULES;GUI_INTERFACES;GUI_RESOURCES;GUI_RESOURCE_FILES;GUI_SOURCES;SOURCES;REQUIRED_PLUGINS;REQUIRED_ON_SERVER;REQUIRED_ON_CLIENT;EXCLUDE_FROM_DEFAULT_TARGET;AUTOLOAD;CS_KITS;EULA"
"" ${ARGN} )
PV_PLUGIN_LIST_CONTAINS(reqired_server_arg "REQUIRED_ON_SERVER" ${ARGN})
......@@ -926,6 +932,23 @@ FUNCTION(ADD_PARAVIEW_PLUGIN NAME VERSION)
endif()
IF(GUI_SRCS OR SM_SRCS OR ARG_SOURCES OR ARG_PYTHON_MODULES)
set (plugin_sources
${CMAKE_CURRENT_BINARY_DIR}/${PLUGIN_NAME}_Plugin.cxx
${CMAKE_CURRENT_BINARY_DIR}/${PLUGIN_NAME}_Plugin.h)
# handle EULA
if (ARG_EULA)
set(PLUGIN_HAS_EULA 1)
vtk_encode_string(
INPUT ${ARG_EULA}
NAME ${PLUGIN_NAME}_EULA
HEADER_OUTPUT PLUGIN_EULA_HEADER
SOURCE_OUTPUT PLUGIN_EULA_SOURCE)
list(APPEND plugin_sources ${PLUGIN_EULA_SOURCE} ${PLUGIN_EULA_HEADER})
else()
set(PLUGIN_HAS_EULA 0)
endif()
CONFIGURE_FILE(
${ParaView_CMAKE_DIR}/pqParaViewPlugin.h.in
${CMAKE_CURRENT_BINARY_DIR}/${PLUGIN_NAME}_Plugin.h @ONLY)
......@@ -933,17 +956,13 @@ FUNCTION(ADD_PARAVIEW_PLUGIN NAME VERSION)
${ParaView_CMAKE_DIR}/pqParaViewPlugin.cxx.in
${CMAKE_CURRENT_BINARY_DIR}/${PLUGIN_NAME}_Plugin.cxx @ONLY)
SET (plugin_sources
${CMAKE_CURRENT_BINARY_DIR}/${PLUGIN_NAME}_Plugin.cxx
${CMAKE_CURRENT_BINARY_DIR}/${PLUGIN_NAME}_Plugin.h
)
IF (plugin_type_gui)
set (__plugin_sources_tmp)
QT5_WRAP_CPP(__plugin_sources_tmp ${CMAKE_CURRENT_BINARY_DIR}/${PLUGIN_NAME}_Plugin.h)
SET (plugin_sources ${plugin_sources} ${__plugin_sources_tmp})
ENDIF ()
if (MSVC)
if (MSVC)
# Do not generate manifests for the plugins - caused issues loading plugins
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} /MANIFEST:NO")
endif()
......
......@@ -36,6 +36,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@EXTRA_INCLUDES@
#if @PLUGIN_HAS_EULA@ // PLUGIN_HAS_EULA
#include "@PLUGIN_EULA_HEADER@"
#endif
namespace
{
// This ensures that when the shared library for this plugin is
......@@ -183,6 +187,16 @@ void @PLUGIN_NAME@_Plugin::GetPythonSourceList(std::vector<std::string>& modules
#endif
}
//-----------------------------------------------------------------------------
const char* @PLUGIN_NAME@_Plugin::GetEULA()
{
#if @PLUGIN_HAS_EULA@ // PLUGIN_HAS_EULA
return @PLUGIN_NAME@_EULA;
#else
return nullptr;
#endif
}
//-----------------------------------------------------------------------------
// Mark this as a ParaView-ServerManager plugin.
PV_PLUGIN_EXPORT(@PLUGIN_NAME@, @PLUGIN_NAME@_Plugin)
......@@ -109,6 +109,8 @@ public:
return "@PLUGIN_REQUIRED_PLUGINS@";
}
const char* GetEULA() override;
// Description:
// Provides access to binary resources compiled into the plugin.
// This is primarily used to compile in icons and compressed help project
......
......@@ -15,15 +15,27 @@
#include "vtkPVPlugin.h"
#include "vtkPVPluginTracker.h"
#include "vtkProcessModule.h"
#include <vtksys/SystemTools.hxx>
#include <cassert>
#include <sstream>
vtkPVPlugin::EULAConfirmationCallback vtkPVPlugin::EULAConfirmationCallbackPtr = nullptr;
//-----------------------------------------------------------------------------
void vtkPVPlugin::ImportPlugin(vtkPVPlugin* plugin)
{
// Register the plugin with the plugin manager on the current process. That
// will kick in the code to process the plugin e.g. initialize CSInterpreter,
// load XML etc.
vtkPVPluginTracker::GetInstance()->RegisterPlugin(plugin);
assert(plugin != nullptr);
// If plugin has an EULA, confirm it before proceeding.
if (plugin->GetEULA() == nullptr || vtkPVPlugin::ConfirmEULA(plugin))
{
// Register the plugin with the plugin manager on the current process. That
// will kick in the code to process the plugin e.g. initialize CSInterpreter,
// load XML etc.
vtkPVPluginTracker::GetInstance()->RegisterPlugin(plugin);
}
}
//-----------------------------------------------------------------------------
......@@ -51,3 +63,32 @@ void vtkPVPlugin::SetFileName(const char* filename)
void vtkPVPlugin::GetBinaryResources(std::vector<std::string>&)
{
}
//-----------------------------------------------------------------------------
void vtkPVPlugin::SetEULAConfirmationCallback(vtkPVPlugin::EULAConfirmationCallback ptr)
{
vtkPVPlugin::EULAConfirmationCallbackPtr = ptr;
}
//-----------------------------------------------------------------------------
bool vtkPVPlugin::ConfirmEULA(vtkPVPlugin* plugin)
{
vtkProcessModule* pm = vtkProcessModule::GetProcessModule();
if (pm->GetPartitionId() == 0 && plugin->GetEULA() != nullptr)
{
if (vtkPVPlugin::EULAConfirmationCallbackPtr != nullptr)
{
return vtkPVPlugin::EULAConfirmationCallbackPtr(plugin);
}
std::ostringstream str;
str << "-----------------------------------------------------" << endl
<< " By loading the '" << plugin->GetPluginName()
<< "' plugin you have accepted the EULA shipped with it." << endl
<< " If that is not acceptable, please restart the application without loading " << endl
<< " the '" << plugin->GetPluginName() << "' plugin." << endl;
str << "-----------------------------------------------------" << endl;
vtkOutputWindowDisplayText(str.str().c_str());
}
return true;
}
......@@ -49,10 +49,6 @@
class VTKPVCLIENTSERVERCORECORE_EXPORT vtkPVPlugin
{
char* FileName;
void SetFileName(const char* filename);
friend class vtkPVPluginLoader;
public:
vtkPVPlugin();
virtual ~vtkPVPlugin();
......@@ -84,6 +80,11 @@ public:
*/
virtual const char* GetRequiredPlugins() = 0;
/**
* Returns EULA for the plugin, if any. If none, this will return nullptr.
*/
virtual const char* GetEULA() = 0;
/**
* Provides access to binary resources compiled into the plugin.
* This is primarily used to compile in icons and compressed help project
......@@ -97,8 +98,45 @@ public:
* This must only be called after the application has initialized, more
* specifically, all plugin managers have been created and they have
* registered their callbacks.
*
* Note, if the plugin has EULA and the user declines the EULA, the import
* request will be ignored, and the plugin won't be imported. This does not
* mean, however, that the plugin won't have any side effects as the plugin
* library can have singletons that get initialized on library load.
*/
static void ImportPlugin(vtkPVPlugin* plugin);
/**
* Type for EULAConfirmationCallback
*/
typedef bool (*EULAConfirmationCallback)(vtkPVPlugin*);
//@{
/**
* Get/Set the static callback to call to confirm EULA
*/
static void SetEULAConfirmationCallback(EULAConfirmationCallback callback);
static EULAConfirmationCallback GetEULAConfirmationCallback();
//@}
private:
/**
* Called to confirm EULA in `ImportPlugin` if the plugin has a non-empty EULA.
* Based on whether EULAConfirmationCallback is specified, this will
* accept the EULA and print a message on the terminal or prompt the user via
* the callback to accept the EULA.
*/
static bool ConfirmEULA(vtkPVPlugin* plugin);
char* FileName;
void SetFileName(const char* filename);
static EULAConfirmationCallback EULAConfirmationCallbackPtr;
friend class vtkPVPluginLoader;
private:
vtkPVPlugin(const vtkPVPlugin&) = delete;
void operator=(const vtkPVPlugin&) = delete;
};
//@}
......
......@@ -132,6 +132,8 @@ public:
{
return NULL;
}
const char* GetEULA() override { return nullptr; }
};
// Cleans successfully opened libs when the application quits.
......
......@@ -33,52 +33,27 @@ void PARAVIEW_CSSTREAMS_INITIALIZE(vtkClientServerInterpreter* interp)
class vtkPVInitializerPlugin : public vtkPVPlugin,
public vtkPVServerManagerPluginInterface
{
// Description:
// Returns the name for this plugin.
virtual const char* GetPluginName()
{return "vtkPVInitializerPlugin"; }
// Description:
// Returns the version for this plugin.
virtual const char* GetPluginVersionString() { return "0.0"; }
// Description:
// Returns true if this plugin is required on the server.
virtual bool GetRequiredOnServer() { return false; }
// Description:
// Returns true if this plugin is required on the client.
virtual bool GetRequiredOnClient() {return false;}
// Description:
// Returns a ';' separated list of plugin names required by this plugin.
virtual const char* GetRequiredPlugins() { return ""; }
// Description:
// Provides access to binary resources compiled into the plugin.
// This is primarily used to compile in icons and compressed help project
// (qch) files into plugins.
virtual void GetBinaryResources(std::vector<std::string>&) {}
// Description:
// Obtain the server-manager configuration xmls, if any.
virtual void GetXMLs(std::vector<std::string> &xmls)
{
const char* GetPluginName() override { return "vtkPVInitializerPlugin"; }
const char* GetPluginVersionString() override { return "0.0"; }
bool GetRequiredOnServer() override { return false; }
bool GetRequiredOnClient() override { return false; }
const char* GetRequiredPlugins() override { return ""; }
void GetBinaryResources(std::vector<std::string>&) override { }
const char* GetEULA() override { return nullptr; }
void GetXMLs(std::vector<std::string> &xmls) override
{
(void) xmls;
char* init_string = NULL;
@xml_init_code@
(void) init_string;
}
}
// Description:
// Returns the callback function to call to initialize the interpretor for the
// new vtk/server-manager classes added by this plugin. Returning NULL is
// perfectly valid.
virtual vtkClientServerInterpreterInitializer::InterpreterInitializationCallback
GetInitializeInterpreterCallback()
{
return PARAVIEW_CSSTREAMS_INITIALIZE;
}
vtkClientServerInterpreterInitializer::InterpreterInitializationCallback
GetInitializeInterpreterCallback() override
{
return PARAVIEW_CSSTREAMS_INITIALIZE;
}
};
void PARAVIEW_INITIALIZE()
......
# This plugin is simply for testing EULA support for plugins.
# This plugin is not built when BUILD_TESTING is OFF.
include_directories(${VTK_INCLUDE_DIRS})
add_paraview_plugin(EULATestPlugin "0.1"
REQUIRED_ON_CLIENT
SERVER_MANAGER_XML EULATestPlugin.xml
EULA EULA.txt)
End User License Agreement
==========================
Just kidding! This plugin is fairly useless, and only meant for
testing the EULA mechanism in ParaView.
You can accept or decline as you choose.
<ServerManagerConfiguration>
<!-- empty XML -->
</ServerManagerConfiguration>
if(BUILD_TESTING)
pv_plugin(EULATestPlugin
DESCRIPTION "Plugin to test EULA"
DEFAULT_ENABLED)
endif()
......@@ -304,8 +304,9 @@ if(NOT VTK_LEGACY_REMOVE)
endif()
set (Module_UI_FILES
Resources/UI/pqOutputWidget.ui
Resources/UI/pqFileDialog.ui
Resources/UI/pqOutputWidget.ui
Resources/UI/pqPluginEULADialog.ui
)
set (Module_RESOURCE_FILES
......
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PluginEULADialog</class>
<widget class="QDialog" name="PluginEULADialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>450</width>
<height>310</height>
</rect>
</property>
<property name="windowTitle">
<string>End User License Agreement</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QTextEdit" name="textEdit">
<property name="readOnly">
<bool>true</bool>
</property>
<property name="html">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;EULA for plugin&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::No|QDialogButtonBox::Yes</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>PluginEULADialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>PluginEULADialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>
......@@ -30,14 +30,18 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
========================================================================*/
#include "pqPluginManager.h"
#include "ui_pqPluginEULADialog.h"
#include "pqApplicationCore.h"
#include "pqCoreUtilities.h"
#include "pqDebug.h"
#include "pqObjectBuilder.h"
#include "pqServer.h"
#include "pqServerConfiguration.h"
#include "pqServerManagerModel.h"
#include "pqSettings.h"
#include "vtkPVPlugin.h"
#include "vtkPVPluginLoader.h"
#include "vtkPVPluginsInformation.h"
#include "vtkSMPluginLoaderProxy.h"
#include "vtkSMPluginManager.h"
......@@ -49,7 +53,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <QCoreApplication>
#include <QPointer>
#include <QPushButton>
#include <QSet>
#include <QTextStream>
#include <sstream>
class pqPluginManager::pqInternals
......@@ -105,6 +111,10 @@ static QString pqPluginManagerSettingsKeyForLocal()
pqPluginManager::pqPluginManager(QObject* parentObject)
: Superclass(parentObject)
{
// setup EULA confirmation callback. Note that is still too late for auto-load
// plugins. For auto-load plugins, the EULA is always auto-accepted.
vtkPVPlugin::SetEULAConfirmationCallback(pqPluginManager::confirmEULA);
this->Internals = new pqInternals();
pqServerManagerModel* smmodel = pqApplicationCore::instance()->getServerManagerModel();
......@@ -295,3 +305,39 @@ bool pqPluginManager::verifyPlugins(pqServer* activeServer)
vtkPVPluginsInformation* remote_info = this->loadedExtensions(activeServer, true);
return vtkPVPluginsInformation::PluginRequirementsSatisfied(local_info, remote_info);
}
//-----------------------------------------------------------------------------
bool pqPluginManager::confirmEULA(vtkPVPlugin* plugin)
{
Q_ASSERT(plugin->GetEULA() != nullptr);
pqSettings* settings = pqApplicationCore::instance()->settings();
QString pluginKey;
QTextStream(&pluginKey) << "EULAConfirmation-" << plugin->GetPluginName() << "-"
<< plugin->GetPluginVersionString() << "-Confirmed";
if (settings->value(pluginKey, false).toBool() == true)
{
// previously accepted.
return true;
}
QDialog dialog(pqCoreUtilities::mainWidget());
Ui::PluginEULADialog ui;
ui.setupUi(&dialog);
ui.buttonBox->button(QDialogButtonBox::Yes)->setText("Accept");
ui.buttonBox->button(QDialogButtonBox::No)->setText("Decline");
ui.buttonBox->button(QDialogButtonBox::No)->setDefault(true);
dialog.setWindowTitle(
QString("End User License Agreement for '%1'").arg(plugin->GetPluginName()));
ui.textEdit->setText(plugin->GetEULA());
if (dialog.exec() == QDialog::Accepted)
{
settings->setValue(pluginKey, true);
return true;
}
return false;
}
......@@ -38,8 +38,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <QStringList>
class pqServer;
class vtkSMPluginManager;
class vtkPVPlugin;
class vtkPVPluginsInformation;
class vtkSMPluginManager;
/**
* pqPluginManager works with vtkSMPluginManager to keep track for plugins
......@@ -147,6 +148,12 @@ protected slots:
private:
class pqInternals;
pqInternals* Internals;
/**
* Callback passed on to `vtkPVPluginLoader::SetEULAConfirmationCallback` to
* confirm EULA for locally loaded plugins.
*/
static bool confirmEULA(vtkPVPlugin* plugin);
};
#endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment