From 86550e983708767fd9d066718797f4bf652f9d38 Mon Sep 17 00:00:00 2001 From: Denis Rouzaud Date: Mon, 30 Sep 2019 13:45:54 +0200 Subject: [PATCH] create a method in QgsGui to determine if running Python macros is allowed --- python/core/auto_additions/qgis.py | 5 +- python/core/auto_generated/qgis.sip.in | 3 +- python/core/core_auto.sip | 2 +- .../gui/auto_generated/qgisinterface.sip.in | 1 - python/gui/auto_generated/qgsgui.sip.in | 1 + src/app/qgisapp.cpp | 41 ++++------- src/app/qgisapp.h | 1 - src/core/qgis.h | 5 +- src/gui/qgisinterface.h | 1 - src/gui/qgsattributeform.cpp | 4 +- src/gui/qgsgui.cpp | 73 ++++++++++++++++++- src/gui/qgsgui.h | 9 +++ 12 files changed, 104 insertions(+), 42 deletions(-) diff --git a/python/core/auto_additions/qgis.py b/python/core/auto_additions/qgis.py index 25b3925dd8c..ee022593ad9 100644 --- a/python/core/auto_additions/qgis.py +++ b/python/core/auto_additions/qgis.py @@ -1,9 +1,10 @@ # The following has been generated automatically from src/core/qgis.h # monkey patching scoped based enum Qgis.PythonMacroMode.Never.__doc__ = "Macros are never run" -Qgis.PythonMacroMode.Ask.__doc__ = "User is prompt before runnning" +Qgis.PythonMacroMode.Ask.__doc__ = "User is prompt before running" Qgis.PythonMacroMode.SessionOnly.__doc__ = "Only during this session" Qgis.PythonMacroMode.Always.__doc__ = "Macros are always run" -Qgis.PythonMacroMode.__doc__ = 'Authorisation to run Python Macros\n\n.. versionadded:: 3.10\n\n' + '* ``Never``: ' + Qgis.PythonMacroMode.Never.__doc__ + '\n' + '* ``Ask``: ' + Qgis.PythonMacroMode.Ask.__doc__ + '\n' + '* ``SessionOnly``: ' + Qgis.PythonMacroMode.SessionOnly.__doc__ + '\n' + '* ``Always``: ' + Qgis.PythonMacroMode.Always.__doc__ +Qgis.PythonMacroMode.NotForThisSession.__doc__ = "Macros will not be run for this session" +Qgis.PythonMacroMode.__doc__ = 'Authorisation to run Python Macros\n\n.. versionadded:: 3.10\n\n' + '* ``Never``: ' + Qgis.PythonMacroMode.Never.__doc__ + '\n' + '* ``Ask``: ' + Qgis.PythonMacroMode.Ask.__doc__ + '\n' + '* ``SessionOnly``: ' + Qgis.PythonMacroMode.SessionOnly.__doc__ + '\n' + '* ``Always``: ' + Qgis.PythonMacroMode.Always.__doc__ + '\n' + '* ``NotForThisSession``: ' + Qgis.PythonMacroMode.NotForThisSession.__doc__ # -- Qgis.PythonMacroMode.baseClass = Qgis diff --git a/python/core/auto_generated/qgis.sip.in b/python/core/auto_generated/qgis.sip.in index c6c343b092a..a96645842a2 100644 --- a/python/core/auto_generated/qgis.sip.in +++ b/python/core/auto_generated/qgis.sip.in @@ -70,7 +70,8 @@ The Qgis class provides global constants for use throughout the application. Never, Ask, SessionOnly, - Always + Always, + NotForThisSession, }; static const double DEFAULT_SEARCH_RADIUS_MM; diff --git a/python/core/core_auto.sip b/python/core/core_auto.sip index 1d20bd28f77..54f6ff45dd5 100644 --- a/python/core/core_auto.sip +++ b/python/core/core_auto.sip @@ -5,7 +5,6 @@ %Include auto_generated/expression/qgsexpressionnodeimpl.sip %Include auto_generated/expression/qgsexpressionfunction.sip %Include auto_generated/qgstessellator.sip -%Include auto_generated/qgis.sip %Include auto_generated/qgsabstractproviderconnection.sip %Include auto_generated/qgsabstractdatabaseproviderconnection.sip %Include auto_generated/qgsaction.sip @@ -335,6 +334,7 @@ %Include auto_generated/validity/qgsvaliditycheckregistry.sip %Include auto_generated/gps/qgsqtlocationconnection.sip %Include auto_generated/gps/qgsgpsconnectionregistry.sip +%Include auto_generated/qgis.sip %Include auto_generated/qgsabstractcontentcache.sip %Include auto_generated/qgsapplication.sip %Include auto_generated/qgsactionscoperegistry.sip diff --git a/python/gui/auto_generated/qgisinterface.sip.in b/python/gui/auto_generated/qgisinterface.sip.in index 8d933847ba0..6f4bbf2a1c6 100644 --- a/python/gui/auto_generated/qgisinterface.sip.in +++ b/python/gui/auto_generated/qgisinterface.sip.in @@ -640,7 +640,6 @@ This represents the current layer tree group and index where newly added map lay .. versionadded:: 3.10 %End - public slots: // TODO: do these functions really need to be slots? diff --git a/python/gui/auto_generated/qgsgui.sip.in b/python/gui/auto_generated/qgsgui.sip.in index 5f868f1dd9d..aa1ff3d65fa 100644 --- a/python/gui/auto_generated/qgsgui.sip.in +++ b/python/gui/auto_generated/qgsgui.sip.in @@ -159,6 +159,7 @@ Returns the screen at the given global ``point`` (pixel). .. versionadded:: 3.10 %End + private: QgsGui( const QgsGui &other ); }; diff --git a/src/app/qgisapp.cpp b/src/app/qgisapp.cpp index d3156765229..3785265887e 100644 --- a/src/app/qgisapp.cpp +++ b/src/app/qgisapp.cpp @@ -2096,11 +2096,19 @@ void QgisApp::readSettings() // Read legacy settings readRecentProjects(); - // this is a new session! reset enable macros value to "ask" - // whether set to "just for this session" - if ( settings.value( QStringLiteral( "qgis/enableMacros" ), 1 ).toInt() == 2 ) + // this is a new session, reset enable macros value when they are set for session + Qgis::PythonMacroMode macroMode = settings.enumValue( QStringLiteral( "qgis/enableMacros" ), Qgis::PythonMacroMode::Ask ); + switch ( macroMode ) { - settings.setValue( QStringLiteral( "qgis/enableMacros" ), 1 ); + case Qgis::PythonMacroMode::NotForThisSession: + case Qgis::PythonMacroMode::SessionOnly: + settings.setEnumValue( QStringLiteral( "qgis/enableMacros" ), Qgis::PythonMacroMode::Ask ); + break; + + case Qgis::PythonMacroMode::Always: + case Qgis::PythonMacroMode::Never: + case Qgis::PythonMacroMode::Ask: + break; } } @@ -6297,29 +6305,8 @@ bool QgisApp::addProject( const QString &projectFile ) } else if ( enableMacros == Qgis::PythonMacroMode::Ask ) { - // create the notification widget for macros - QToolButton *btnEnableMacros = new QToolButton(); - btnEnableMacros->setText( tr( "Enable Macros" ) ); - btnEnableMacros->setStyleSheet( QStringLiteral( "background-color: rgba(255, 255, 255, 0); color: black; text-decoration: underline;" ) ); - btnEnableMacros->setCursor( Qt::PointingHandCursor ); - btnEnableMacros->setSizePolicy( QSizePolicy::Maximum, QSizePolicy::Preferred ); - - QgsMessageBarItem *macroMsg = new QgsMessageBarItem( - tr( "Security warning" ), - tr( "project macros have been disabled." ), - btnEnableMacros, - Qgis::Warning, - 0, - mInfoBar ); - - connect( btnEnableMacros, &QToolButton::clicked, this, [this, macroMsg] - { - enableProjectMacros(); - mInfoBar->popWidget( macroMsg ); - } ); - - // display the macros notification widget - mInfoBar->pushItem( macroMsg ); + auto lambda = []() {QgisApp::instance()->enableProjectMacros();}; + QgsGui::pythonMacroAllowed( lambda, mInfoBar ); } } } diff --git a/src/app/qgisapp.h b/src/app/qgisapp.h index 31881d1cc4f..bc7e7c2d8e6 100644 --- a/src/app/qgisapp.h +++ b/src/app/qgisapp.h @@ -237,7 +237,6 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow /** * Attempts to run a Python script * \param filePath full path to Python script - * \since QGIS 2.7 */ void runScript( const QString &filePath ); diff --git a/src/core/qgis.h b/src/core/qgis.h index cf38539498d..8827d616b89 100644 --- a/src/core/qgis.h +++ b/src/core/qgis.h @@ -102,9 +102,10 @@ class CORE_EXPORT Qgis enum class PythonMacroMode { Never = 0, //!< Macros are never run - Ask = 1, //!< User is prompt before runnning + Ask = 1, //!< User is prompt before running SessionOnly = 2, //!< Only during this session - Always = 3 //!< Macros are always run + Always = 3, //!< Macros are always run + NotForThisSession, //!< Macros will not be run for this session }; Q_ENUM( PythonMacroMode ) diff --git a/src/gui/qgisinterface.h b/src/gui/qgisinterface.h index f1b9e4478f7..f7e9a0f6a1a 100644 --- a/src/gui/qgisinterface.h +++ b/src/gui/qgisinterface.h @@ -570,7 +570,6 @@ class GUI_EXPORT QgisInterface : public QObject */ virtual QgsLayerTreeRegistryBridge::InsertionPoint layerTreeInsertionPoint() = 0; - public slots: // TODO: do these functions really need to be slots? /* Exposed functions */ diff --git a/src/gui/qgsattributeform.cpp b/src/gui/qgsattributeform.cpp index 001361ecc1e..033c79991a6 100644 --- a/src/gui/qgsattributeform.cpp +++ b/src/gui/qgsattributeform.cpp @@ -20,6 +20,7 @@ #include "qgsattributeformrelationeditorwidget.h" #include "qgseditorwidgetregistry.h" #include "qgsfeatureiterator.h" +#include "qgsgui.h" #include "qgsproject.h" #include "qgspythonrunner.h" #include "qgsrelationwidgetwrapper.h" @@ -1744,13 +1745,12 @@ void QgsAttributeForm::initPython() case QgsEditFormConfig::CodeSourceEnvironment: case QgsEditFormConfig::CodeSourceNone: - default: // Nothing to do: the function code should be already in the environment break; } // If we have a function code, run it - if ( ! initCode.isEmpty() ) + if ( !initCode.isEmpty() && QgsGui::pythonMacroAllowed() ) { QgsPythonRunner::run( initCode ); } diff --git a/src/gui/qgsgui.cpp b/src/gui/qgsgui.cpp index ff4df0ae8b8..613305487fe 100644 --- a/src/gui/qgsgui.cpp +++ b/src/gui/qgsgui.cpp @@ -15,6 +15,11 @@ * * ***************************************************************************/ + +#include +#include +#include + #include "qgsgui.h" #include "qgseditorwidgetregistry.h" #include "qgslayertreeembeddedwidgetregistry.h" @@ -49,9 +54,9 @@ #include "qgsproviderregistry.h" #include "qgsproviderguiregistry.h" #include "qgsprojectstorageguiregistry.h" +#include "qgsmessagebar.h" +#include "qgsmessagebaritem.h" -#include -#include QgsGui *QgsGui::instance() { @@ -229,6 +234,66 @@ QgsGui::QgsGui() mWidgetStateHelper = new QgsWidgetStateHelper(); mProcessingRecentAlgorithmLog = new QgsProcessingRecentAlgorithmLog(); mProcessingGuiRegistry = new QgsProcessingGuiRegistry(); - - +} + +bool QgsGui::pythonMacroAllowed( void ( *lambda )(), QgsMessageBar *messageBar ) +{ + Qgis::PythonMacroMode macroMode = QgsSettings().enumValue( QStringLiteral( "qgis/enableMacros" ), Qgis::PythonMacroMode::Ask ); + + switch ( macroMode ) + { + case Qgis::PythonMacroMode::SessionOnly: + case Qgis::PythonMacroMode::Always: + return true; + case Qgis::PythonMacroMode::Never: + case Qgis::PythonMacroMode::NotForThisSession: + return false; + case Qgis::PythonMacroMode::Ask: + if ( !lambda ) + { + QMessageBox msgBox( QMessageBox::Information, "Python Macros", + tr( "Python macros are currently disabled. Do you allow this macro to run?" ) ); + QAbstractButton *stopSessionButton = msgBox.addButton( tr( "Don't ask anymore" ), QMessageBox::DestructiveRole ); + msgBox.addButton( tr( "No" ), QMessageBox::NoRole ); + QAbstractButton *yesButton = msgBox.addButton( tr( "Yes" ), QMessageBox::YesRole ); + QAbstractButton *clicked = msgBox.clickedButton(); + if ( clicked == stopSessionButton ) + { + QgsSettings().setEnumValue( QStringLiteral( "qgis/enableMacros" ), Qgis::PythonMacroMode::NotForThisSession ); + } + return clicked == yesButton; + } + else + { + // create the notification widget for macros + Q_ASSERT( messageBar ); + if ( messageBar ) + { + QToolButton *btnEnableMacros = new QToolButton(); + btnEnableMacros->setText( tr( "Enable Macros" ) ); + btnEnableMacros->setStyleSheet( QStringLiteral( "background-color: rgba(255, 255, 255, 0); color: black; text-decoration: underline;" ) ); + btnEnableMacros->setCursor( Qt::PointingHandCursor ); + btnEnableMacros->setSizePolicy( QSizePolicy::Maximum, QSizePolicy::Preferred ); + + QgsMessageBarItem *macroMsg = new QgsMessageBarItem( + tr( "Security warning" ), + tr( "Python macros cannot currently be run." ), + btnEnableMacros, + Qgis::Warning, + 0, + messageBar ); + + connect( btnEnableMacros, &QToolButton::clicked, [ = ]() + { + lambda(); + messageBar->popWidget( macroMsg ); + } ); + + // display the macros notification widget + messageBar->pushItem( macroMsg ); + } + + return false; + } + } } diff --git a/src/gui/qgsgui.h b/src/gui/qgsgui.h index c37cb4cb9ac..f28c7154d71 100644 --- a/src/gui/qgsgui.h +++ b/src/gui/qgsgui.h @@ -37,6 +37,7 @@ class QgsWindowManagerInterface; class QgsDataItemGuiProviderRegistry; class QgsProviderGuiRegistry; class QgsProjectStorageGuiRegistry; +class QgsMessageBar; /** * \ingroup gui @@ -192,6 +193,14 @@ class GUI_EXPORT QgsGui : public QObject */ static QScreen *findScreenAt( QPoint point ); + /** + * Returns true if python macros are currently allowed to be run + * If the global option is to ask user, a modal dialog will be shown + * \param lambda a pointer to a lambda method. If specified, the dialog is not modal, + * a message bar is shown with a button to enable macro and run the lambda + */ + static bool pythonMacroAllowed( void ( *lambda )() = nullptr, QgsMessageBar *messageBar = nullptr ) SIP_SKIP; + private: QgsGui();