From b516a49b9e8856567997c1f941dec8d69a749653 Mon Sep 17 00:00:00 2001 From: Denis Rouzaud Date: Fri, 11 Sep 2020 10:57:22 +0200 Subject: [PATCH 01/15] create helper method QgsCheckableComboBox::addItemWithCheckState --- python/gui/auto_generated/qgscheckablecombobox.sip.in | 9 +++++++++ src/gui/qgscheckablecombobox.cpp | 6 ++++++ src/gui/qgscheckablecombobox.h | 8 ++++++++ 3 files changed, 23 insertions(+) diff --git a/python/gui/auto_generated/qgscheckablecombobox.sip.in b/python/gui/auto_generated/qgscheckablecombobox.sip.in index fbdb1a28caf4..f4d5be712213 100644 --- a/python/gui/auto_generated/qgscheckablecombobox.sip.in +++ b/python/gui/auto_generated/qgscheckablecombobox.sip.in @@ -62,6 +62,15 @@ no items selected. :param text: default text .. seealso:: :py:func:`defaultText` +%End + + void addItemWithCheckState( const QString &text, Qt::CheckState state, const QVariant &userData = QVariant() ); +%Docstring +Adds an item to the combobox with the given ``text``, check ``state`` +and containing the specified ``userData`` (stored in the Qt.UserRole). +The item is appended to the list of existing items. + +.. versionadded:: 3.16 %End QStringList checkedItems() const; diff --git a/src/gui/qgscheckablecombobox.cpp b/src/gui/qgscheckablecombobox.cpp index 0eec32c91eeb..a6a82afae6bb 100644 --- a/src/gui/qgscheckablecombobox.cpp +++ b/src/gui/qgscheckablecombobox.cpp @@ -134,6 +134,12 @@ void QgsCheckableComboBox::setDefaultText( const QString &text ) } } +void QgsCheckableComboBox::addItemWithCheckState( const QString &text, Qt::CheckState state, const QVariant &userData ) +{ + QComboBox::addItem( text, userData ); + setItemCheckState( count(), state ); +} + QStringList QgsCheckableComboBox::checkedItems() const { QStringList items; diff --git a/src/gui/qgscheckablecombobox.h b/src/gui/qgscheckablecombobox.h index f6d6bb817114..5c884b7c6d56 100644 --- a/src/gui/qgscheckablecombobox.h +++ b/src/gui/qgscheckablecombobox.h @@ -163,6 +163,14 @@ class GUI_EXPORT QgsCheckableComboBox : public QComboBox */ void setDefaultText( const QString &text ); + /** + * Adds an item to the combobox with the given \a text, check \a state + * and containing the specified \a userData (stored in the Qt::UserRole). + * The item is appended to the list of existing items. + * \since QGIS 3.16 + */ + void addItemWithCheckState( const QString &text, Qt::CheckState state, const QVariant &userData = QVariant() ); + /** * Returns currently checked items. * \see setCheckedItems() From 19d4b52f1069b1c055bf0ae76dabe54642ea7fbe Mon Sep 17 00:00:00 2001 From: Denis Rouzaud Date: Sun, 13 Sep 2020 17:15:25 +0200 Subject: [PATCH 02/15] Add WMS/WFS to QgsField::ConfigurationFlag --- python/gui/auto_generated/qgscheckablecombobox.sip.in | 4 +++- src/core/qgsfield.h | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/python/gui/auto_generated/qgscheckablecombobox.sip.in b/python/gui/auto_generated/qgscheckablecombobox.sip.in index f4d5be712213..db9d202ea695 100644 --- a/python/gui/auto_generated/qgscheckablecombobox.sip.in +++ b/python/gui/auto_generated/qgscheckablecombobox.sip.in @@ -66,7 +66,7 @@ no items selected. void addItemWithCheckState( const QString &text, Qt::CheckState state, const QVariant &userData = QVariant() ); %Docstring -Adds an item to the combobox with the given ``text``, check ``state`` +Adds an item to the combobox with the given ``text``, check ``state`` (stored in the Qt.CheckStateRole) and containing the specified ``userData`` (stored in the Qt.UserRole). The item is appended to the list of existing items. @@ -122,6 +122,8 @@ Toggles the item check state .. seealso:: :py:func:`setItemCheckState` %End + QgsCheckableItemModel *model() const; + virtual void hidePopup(); %Docstring diff --git a/src/core/qgsfield.h b/src/core/qgsfield.h index a0e878993c95..baf721c493d4 100644 --- a/src/core/qgsfield.h +++ b/src/core/qgsfield.h @@ -81,7 +81,9 @@ class CORE_EXPORT QgsField { None = 0, //!< No flag is defined Searchable = 0x1, //!< Defines if the field is searchable (used in the locator search for instance) - DefaultFlags = Searchable, //!< Default set of flags for a field + Wms = 0x2, //!< Fields is available if layer is served as WMS + Wfs = 0x3, //!< Fields is available if layer is served as WFS + DefaultFlags = Searchable | Wms | Wfs, //!< Default set of flags for a field }; Q_ENUM( ConfigurationFlag ) Q_DECLARE_FLAGS( ConfigurationFlags, ConfigurationFlag ) From 4fd940a352e35f97c146bf66238f777a2a7b1478 Mon Sep 17 00:00:00 2001 From: Denis Rouzaud Date: Mon, 14 Sep 2020 09:44:04 +0200 Subject: [PATCH 03/15] use configuration flags to save WMS/WFS from vector layer --- .../core/auto_generated/qgsvectorlayer.sip.in | 9 +-- src/core/qgsvectorlayer.cpp | 66 +++++++------------ src/core/qgsvectorlayer.h | 14 ++-- 3 files changed, 39 insertions(+), 50 deletions(-) diff --git a/python/core/auto_generated/qgsvectorlayer.sip.in b/python/core/auto_generated/qgsvectorlayer.sip.in index b40402cad882..68d33b516c0b 100644 --- a/python/core/auto_generated/qgsvectorlayer.sip.in +++ b/python/core/auto_generated/qgsvectorlayer.sip.in @@ -1968,22 +1968,22 @@ Convenience function that returns the attribute alias if defined or the field na Returns a map of field name to attribute alias %End - QSet excludeAttributesWms() const; + QSet excludeAttributesWms() /Deprecated/ const; %Docstring A set of attributes that are not advertised in WMS requests with QGIS server. %End - void setExcludeAttributesWms( const QSet &att ); + void setExcludeAttributesWms( const QSet &att ) /Deprecated/; %Docstring A set of attributes that are not advertised in WMS requests with QGIS server. %End - QSet excludeAttributesWfs() const; + QSet excludeAttributesWfs() /Deprecated/ const; %Docstring A set of attributes that are not advertised in WFS requests with QGIS server. %End - void setExcludeAttributesWfs( const QSet &att ); + void setExcludeAttributesWfs( const QSet &att ) /Deprecated/; %Docstring A set of attributes that are not advertised in WFS requests with QGIS server. %End @@ -2294,6 +2294,7 @@ can also be set. Setting an empty expression will clear any existing expression + void setEditorWidgetSetup( int index, const QgsEditorWidgetSetup &setup ); %Docstring \copydoc editorWidgetSetup diff --git a/src/core/qgsvectorlayer.cpp b/src/core/qgsvectorlayer.cpp index 30941209de63..3929f5568aac 100644 --- a/src/core/qgsvectorlayer.cpp +++ b/src/core/qgsvectorlayer.cpp @@ -274,8 +274,6 @@ QgsVectorLayer *QgsVectorLayer::clone() const layer->setMapTipTemplate( mapTipTemplate() ); layer->setReadOnly( isReadOnly() ); layer->selectByIds( selectedFeatureIds() ); - layer->setExcludeAttributesWms( excludeAttributesWms() ); - layer->setExcludeAttributesWfs( excludeAttributesWfs() ); layer->setAttributeTableConfig( attributeTableConfig() ); layer->setFeatureBlendMode( featureBlendMode() ); layer->setOpacity( opacity() ); @@ -2306,26 +2304,25 @@ bool QgsVectorLayer::readSymbology( const QDomNode &layerNode, QString &errorMes updateFields(); + // Legacy reading for QGIS 3.14 and older projects //Attributes excluded from WMS and WFS - mExcludeAttributesWMS.clear(); - QDomNode excludeWMSNode = layerNode.namedItem( QStringLiteral( "excludeAttributesWMS" ) ); - if ( !excludeWMSNode.isNull() ) + const QList> legacyConfig { - QDomNodeList attributeNodeList = excludeWMSNode.toElement().elementsByTagName( QStringLiteral( "attribute" ) ); - for ( int i = 0; i < attributeNodeList.size(); ++i ) - { - mExcludeAttributesWMS.insert( attributeNodeList.at( i ).toElement().text() ); - } - } - - mExcludeAttributesWFS.clear(); - QDomNode excludeWFSNode = layerNode.namedItem( QStringLiteral( "excludeAttributesWFS" ) ); - if ( !excludeWFSNode.isNull() ) + qMakePair( QStringLiteral( "excludeAttributesWMS" ), QgsField::ConfigurationFlag::Wms ), + qMakePair( QStringLiteral( "excludeAttributesWFS" ), QgsField::ConfigurationFlag::Wfs ) + }; + for ( const auto &config : legacyConfig ) { - QDomNodeList attributeNodeList = excludeWFSNode.toElement().elementsByTagName( QStringLiteral( "attribute" ) ); - for ( int i = 0; i < attributeNodeList.size(); ++i ) + QDomNode excludeNode = layerNode.namedItem( config.first ); + if ( !excludeNode.isNull() ) { - mExcludeAttributesWFS.insert( attributeNodeList.at( i ).toElement().text() ); + QDomNodeList attributeNodeList = excludeNode.toElement().elementsByTagName( QStringLiteral( "attribute" ) ); + for ( int i = 0; i < attributeNodeList.size(); ++i ) + { + QString fieldName = attributeNodeList.at( i ).toElement().text(); + int index = mFields.indexFromName( fieldName ); + setFieldConfigurationFlag( index, config.second, false ); + } } } @@ -2660,30 +2657,6 @@ bool QgsVectorLayer::writeSymbology( QDomNode &node, QDomDocument &doc, QString } node.appendChild( aliasElem ); - //exclude attributes WMS - QDomElement excludeWMSElem = doc.createElement( QStringLiteral( "excludeAttributesWMS" ) ); - QSet::const_iterator attWMSIt = mExcludeAttributesWMS.constBegin(); - for ( ; attWMSIt != mExcludeAttributesWMS.constEnd(); ++attWMSIt ) - { - QDomElement attrElem = doc.createElement( QStringLiteral( "attribute" ) ); - QDomText attrText = doc.createTextNode( *attWMSIt ); - attrElem.appendChild( attrText ); - excludeWMSElem.appendChild( attrElem ); - } - node.appendChild( excludeWMSElem ); - - //exclude attributes WFS - QDomElement excludeWFSElem = doc.createElement( QStringLiteral( "excludeAttributesWFS" ) ); - QSet::const_iterator attWFSIt = mExcludeAttributesWFS.constBegin(); - for ( ; attWFSIt != mExcludeAttributesWFS.constEnd(); ++attWFSIt ) - { - QDomElement attrElem = doc.createElement( QStringLiteral( "attribute" ) ); - QDomText attrText = doc.createTextNode( *attWFSIt ); - attrElem.appendChild( attrText ); - excludeWFSElem.appendChild( attrElem ); - } - node.appendChild( excludeWFSElem ); - //default expressions QDomElement defaultsElem = doc.createElement( QStringLiteral( "defaults" ) ); for ( const QgsField &field : qgis::as_const( mFields ) ) @@ -5493,6 +5466,15 @@ void QgsVectorLayer::setFieldConfigurationFlags( int index, QgsField::Configurat updateFields(); } +void QgsVectorLayer::setFieldConfigurationFlag( int index, QgsField::ConfigurationFlag flag, bool active ) +{ + if ( index < 0 || index >= mFields.count() ) + return; + QgsField::ConfigurationFlags flags = mFields.at( index ).configurationFlags(); + flags.setFlag( flag, active ); + setFieldConfigurationFlags( index, flags ); +} + QgsField::ConfigurationFlags QgsVectorLayer::fieldConfigurationFlags( int index ) const { diff --git a/src/core/qgsvectorlayer.h b/src/core/qgsvectorlayer.h index e600e0a8d986..3e31bfd06cf9 100644 --- a/src/core/qgsvectorlayer.h +++ b/src/core/qgsvectorlayer.h @@ -1840,22 +1840,22 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte /** * A set of attributes that are not advertised in WMS requests with QGIS server. */ - QSet excludeAttributesWms() const { return mExcludeAttributesWMS; } + Q_DECL_DEPRECATED QSet excludeAttributesWms() SIP_DEPRECATED const { return mExcludeAttributesWMS; } /** * A set of attributes that are not advertised in WMS requests with QGIS server. */ - void setExcludeAttributesWms( const QSet &att ) { mExcludeAttributesWMS = att; } + Q_DECL_DEPRECATED void setExcludeAttributesWms( const QSet &att ) SIP_DEPRECATED { mExcludeAttributesWMS = att; } /** * A set of attributes that are not advertised in WFS requests with QGIS server. */ - QSet excludeAttributesWfs() const { return mExcludeAttributesWFS; } + Q_DECL_DEPRECATED QSet excludeAttributesWfs() SIP_DEPRECATED const { return mExcludeAttributesWFS; } /** * A set of attributes that are not advertised in WFS requests with QGIS server. */ - void setExcludeAttributesWfs( const QSet &att ) { mExcludeAttributesWFS = att; } + Q_DECL_DEPRECATED void setExcludeAttributesWfs( const QSet &att ) SIP_DEPRECATED { mExcludeAttributesWFS = att; } /** * Deletes an attribute field (but does not commit it). @@ -2122,6 +2122,12 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte */ void setFieldConfigurationFlags( int index, QgsField::ConfigurationFlags flags ) SIP_SKIP; + /** + * Sets the given configuration \a flag for the field at given \a index to be \a active or not. + * \since QGIS 3.16 + */ + void setFieldConfigurationFlag( int index, QgsField::ConfigurationFlag flag, bool active ) SIP_SKIP; + /** * Returns the configuration flags of the field at given index * \see QgsField::ConfigurationFlag From d273f78c23b2db986b1b9d6b1cfba73194f0bbd6 Mon Sep 17 00:00:00 2001 From: Denis Rouzaud Date: Mon, 14 Sep 2020 09:47:43 +0200 Subject: [PATCH 04/15] use a checkable combo box for fields configuration flags in source fields properties tab --- .../qgscheckablecombobox.sip.in | 7 +++ src/gui/qgscheckablecombobox.cpp | 3 +- src/gui/qgscheckablecombobox.h | 11 +++- src/gui/vector/qgssourcefieldsproperties.cpp | 60 ++++++++----------- 4 files changed, 45 insertions(+), 36 deletions(-) diff --git a/python/gui/auto_generated/qgscheckablecombobox.sip.in b/python/gui/auto_generated/qgscheckablecombobox.sip.in index db9d202ea695..c2c8b876d229 100644 --- a/python/gui/auto_generated/qgscheckablecombobox.sip.in +++ b/python/gui/auto_generated/qgscheckablecombobox.sip.in @@ -123,6 +123,13 @@ Toggles the item check state %End QgsCheckableItemModel *model() const; +%Docstring +Returns the custom item model which handles checking the items + +.. seealso:: :py:class:`QgsCheckableItemModel` + +.. versionadded:: 3.16 +%End virtual void hidePopup(); diff --git a/src/gui/qgscheckablecombobox.cpp b/src/gui/qgscheckablecombobox.cpp index a6a82afae6bb..3375c47142a4 100644 --- a/src/gui/qgscheckablecombobox.cpp +++ b/src/gui/qgscheckablecombobox.cpp @@ -77,8 +77,9 @@ void QgsCheckBoxDelegate::paint( QPainter *painter, const QStyleOptionViewItem & QgsCheckableComboBox::QgsCheckableComboBox( QWidget *parent ) : QComboBox( parent ) , mSeparator( QStringLiteral( ", " ) ) + , mModel( new QgsCheckableItemModel( this ) ) { - setModel( new QgsCheckableItemModel( this ) ); + setModel( mModel ); setItemDelegate( new QgsCheckBoxDelegate( this ) ); QLineEdit *lineEdit = new QLineEdit( this ); diff --git a/src/gui/qgscheckablecombobox.h b/src/gui/qgscheckablecombobox.h index 5c884b7c6d56..991f34894c10 100644 --- a/src/gui/qgscheckablecombobox.h +++ b/src/gui/qgscheckablecombobox.h @@ -164,7 +164,7 @@ class GUI_EXPORT QgsCheckableComboBox : public QComboBox void setDefaultText( const QString &text ); /** - * Adds an item to the combobox with the given \a text, check \a state + * Adds an item to the combobox with the given \a text, check \a state (stored in the Qt::CheckStateRole) * and containing the specified \a userData (stored in the Qt::UserRole). * The item is appended to the list of existing items. * \since QGIS 3.16 @@ -209,6 +209,13 @@ class GUI_EXPORT QgsCheckableComboBox : public QComboBox */ void toggleItemCheckState( int index ); + /** + * Returns the custom item model which handles checking the items + * \see QgsCheckableItemModel + * \since QGIS 3.16 + */ + QgsCheckableItemModel *model() const {return mModel;} + /** * Hides the list of items in the combobox if it is currently * visible and resets the internal state. @@ -268,6 +275,8 @@ class GUI_EXPORT QgsCheckableComboBox : public QComboBox QString mSeparator; QString mDefaultText; + QgsCheckableItemModel *mModel = nullptr; + bool mSkipHide = false; QMenu *mContextMenu = nullptr; diff --git a/src/gui/vector/qgssourcefieldsproperties.cpp b/src/gui/vector/qgssourcefieldsproperties.cpp index a14c48417f9b..8843c3dd5c19 100644 --- a/src/gui/vector/qgssourcefieldsproperties.cpp +++ b/src/gui/vector/qgssourcefieldsproperties.cpp @@ -15,6 +15,7 @@ ***************************************************************************/ #include "qgsaddattrdialog.h" +#include "qgscheckablecombobox.h" #include "qgssourcefieldsproperties.h" #include "qgsvectorlayer.h" #include "qgsproject.h" @@ -272,20 +273,20 @@ void QgsSourceFieldsProperties::setRow( int row, int idx, const QgsField &field else mFieldsList->item( row, AttrNameCol )->setFlags( mFieldsList->item( row, AttrNameCol )->flags() & ~Qt::ItemIsEditable ); - //published WMS/WFS attributes - QTableWidgetItem *wmsAttrItem = new QTableWidgetItem(); - wmsAttrItem->setCheckState( mLayer->excludeAttributesWms().contains( field.name() ) ? Qt::Unchecked : Qt::Checked ); - wmsAttrItem->setFlags( Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable ); - mFieldsList->setItem( row, AttrWMSCol, wmsAttrItem ); - QTableWidgetItem *wfsAttrItem = new QTableWidgetItem(); - wfsAttrItem->setCheckState( mLayer->excludeAttributesWfs().contains( field.name() ) ? Qt::Unchecked : Qt::Checked ); - wfsAttrItem->setFlags( Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable ); - mFieldsList->setItem( row, AttrWFSCol, wfsAttrItem ); - QTableWidgetItem *searchableAttrItem = new QTableWidgetItem(); - searchableAttrItem->setCheckState( mLayer->fieldConfigurationFlags( idx ).testFlag( QgsField::ConfigurationFlag::Searchable ) ? Qt::Checked : Qt::Unchecked ); - searchableAttrItem->setFlags( Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable ); - mFieldsList->setItem( row, AttrSearchableCol, searchableAttrItem ); + // Flags + QgsCheckableComboBox *cb = new QgsCheckableComboBox( mFieldsList ); + const QMap flagList = qgsEnumMap(); + QMap::const_iterator flagIt; + for ( flagIt = flagList.constBegin(); flagIt != flagList.constEnd(); ++flagIt ) + { + if ( flagIt.key() == QgsField::ConfigurationFlag::None || flagIt.key() == QgsField::ConfigurationFlag::DefaultFlags ) + continue; + cb->addItemWithCheckState( flagIt.value(), + mLayer->fieldConfigurationFlags( idx ).testFlag( flagIt.key() ) ? Qt::Unchecked : Qt::Checked, + QVariant::fromValue( flagIt.key() ) ); + } + mFieldsList->setCellWidget( row, AttrSearchableCol, cb ); } bool QgsSourceFieldsProperties::addAttribute( const QgsField &field ) @@ -307,34 +308,25 @@ bool QgsSourceFieldsProperties::addAttribute( const QgsField &field ) void QgsSourceFieldsProperties::apply() { - QSet excludeAttributesWMS, excludeAttributesWFS; - for ( int i = 0; i < mFieldsList->rowCount(); i++ ) { - if ( mFieldsList->item( i, AttrWMSCol )->checkState() == Qt::Unchecked ) - { - excludeAttributesWMS.insert( mFieldsList->item( i, AttrNameCol )->text() ); - } - if ( mFieldsList->item( i, AttrWFSCol )->checkState() == Qt::Unchecked ) - { - excludeAttributesWFS.insert( mFieldsList->item( i, AttrNameCol )->text() ); - } - int idx = mFieldsList->item( i, AttrIdCol )->data( Qt::DisplayRole ).toInt(); QgsField::ConfigurationFlags flags = mLayer->fieldConfigurationFlags( idx ); - if ( mFieldsList->item( i, AttrSearchableCol )->checkState() == Qt::Checked ) - { - flags.setFlag( QgsField::ConfigurationFlag::Searchable, true ); - } - else + + QgsCheckableComboBox *cb = qobject_cast( mFieldsList->cellWidget( i, AttrSearchableCol ) ); + if ( cb ) { - flags.setFlag( QgsField::ConfigurationFlag::Searchable, false ); + QgsCheckableItemModel *model = cb->model(); + for ( int r = 0; r < model->rowCount(); ++r ) + { + QModelIndex index = model->index( r, 0 ); + QgsField::ConfigurationFlag flag = model->data( index, Qt::UserRole ).value(); + bool active = model->data( index, Qt::CheckStateRole ).value() == Qt::Checked ? true : false; + flags.setFlag( flag, active ); + } + mLayer->setFieldConfigurationFlags( idx, flags ); } - mLayer->setFieldConfigurationFlags( idx, flags ); } - - mLayer->setExcludeAttributesWms( excludeAttributesWMS ); - mLayer->setExcludeAttributesWfs( excludeAttributesWFS ); } //SLOTS From de4a293ec5d24589563bdf92fe25680dc99ef53c Mon Sep 17 00:00:00 2001 From: Denis Rouzaud Date: Mon, 14 Sep 2020 11:33:21 +0200 Subject: [PATCH 05/15] use a readable string, fix flag keys, fix opening old project file --- python/core/auto_generated/qgsfield.sip.in | 7 +++ src/core/qgsfield.cpp | 17 ++++++++ src/core/qgsfield.h | 12 +++-- src/core/qgsvectorlayer.cpp | 46 ++++++++++---------- src/gui/qgscheckablecombobox.cpp | 2 +- src/gui/vector/qgssourcefieldsproperties.cpp | 32 ++++++-------- src/gui/vector/qgssourcefieldsproperties.h | 4 +- 7 files changed, 71 insertions(+), 49 deletions(-) diff --git a/python/core/auto_generated/qgsfield.sip.in b/python/core/auto_generated/qgsfield.sip.in index 16acde01af6d..c903d2d97795 100644 --- a/python/core/auto_generated/qgsfield.sip.in +++ b/python/core/auto_generated/qgsfield.sip.in @@ -291,6 +291,13 @@ Sets the alias for the field (the friendly displayed name of the field ). Formats string for display %End + static QString readableConfigurationFlag( ConfigurationFlag flag ); +%Docstring +Returns the reabable and translated value of the configuration flag + +.. versionadded:: 3.16 +%End + bool convertCompatible( QVariant &v ) const; %Docstring diff --git a/src/core/qgsfield.cpp b/src/core/qgsfield.cpp index 51ab7ef900c7..8f2c3fea218b 100644 --- a/src/core/qgsfield.cpp +++ b/src/core/qgsfield.cpp @@ -346,6 +346,23 @@ QString QgsField::displayString( const QVariant &v ) const return v.toString(); } +QString QgsField::readableConfigurationFlag( QgsField::ConfigurationFlag flag ) +{ + switch ( flag ) + { + case ConfigurationFlag::None: + return QObject::tr( "None" ); + case ConfigurationFlag::Searchable: + return QObject::tr( "Searchable" ); + case ConfigurationFlag::Wms: + return QStringLiteral( "WMS" ); + case ConfigurationFlag::Wfs: + return QStringLiteral( "WFS" ); + case ConfigurationFlag::DefaultFlags: + return QObject::tr( "Default flags" ); + } +} + /*************************************************************************** * This class is considered CRITICAL and any change MUST be accompanied with * full unit tests in testqgsfield.cpp. diff --git a/src/core/qgsfield.h b/src/core/qgsfield.h index baf721c493d4..1c1041eee92a 100644 --- a/src/core/qgsfield.h +++ b/src/core/qgsfield.h @@ -80,9 +80,9 @@ class CORE_EXPORT QgsField #endif { None = 0, //!< No flag is defined - Searchable = 0x1, //!< Defines if the field is searchable (used in the locator search for instance) - Wms = 0x2, //!< Fields is available if layer is served as WMS - Wfs = 0x3, //!< Fields is available if layer is served as WFS + Searchable = 1 << 1, //!< Defines if the field is searchable (used in the locator search for instance) + Wms = 1 << 2, //!< Fields is available if layer is served as WMS + Wfs = 1 << 3, //!< Fields is available if layer is served as WFS DefaultFlags = Searchable | Wms | Wfs, //!< Default set of flags for a field }; Q_ENUM( ConfigurationFlag ) @@ -329,6 +329,12 @@ class CORE_EXPORT QgsField //! Formats string for display QString displayString( const QVariant &v ) const; + /** + * Returns the reabable and translated value of the configuration flag + * \since QGIS 3.16 + */ + static QString readableConfigurationFlag( ConfigurationFlag flag ); + #ifndef SIP_RUN /** diff --git a/src/core/qgsvectorlayer.cpp b/src/core/qgsvectorlayer.cpp index 3929f5568aac..0a0f9873718d 100644 --- a/src/core/qgsvectorlayer.cpp +++ b/src/core/qgsvectorlayer.cpp @@ -2304,28 +2304,6 @@ bool QgsVectorLayer::readSymbology( const QDomNode &layerNode, QString &errorMes updateFields(); - // Legacy reading for QGIS 3.14 and older projects - //Attributes excluded from WMS and WFS - const QList> legacyConfig - { - qMakePair( QStringLiteral( "excludeAttributesWMS" ), QgsField::ConfigurationFlag::Wms ), - qMakePair( QStringLiteral( "excludeAttributesWFS" ), QgsField::ConfigurationFlag::Wfs ) - }; - for ( const auto &config : legacyConfig ) - { - QDomNode excludeNode = layerNode.namedItem( config.first ); - if ( !excludeNode.isNull() ) - { - QDomNodeList attributeNodeList = excludeNode.toElement().elementsByTagName( QStringLiteral( "attribute" ) ); - for ( int i = 0; i < attributeNodeList.size(); ++i ) - { - QString fieldName = attributeNodeList.at( i ).toElement().text(); - int index = mFields.indexFromName( fieldName ); - setFieldConfigurationFlag( index, config.second, false ); - } - } - } - // Load editor widget configuration QDomElement widgetsElem = layerNode.namedItem( QStringLiteral( "fieldConfiguration" ) ).toElement(); QDomNodeList fieldConfigurationElementList = widgetsElem.elementsByTagName( QStringLiteral( "field" ) ); @@ -2348,6 +2326,28 @@ bool QgsVectorLayer::readSymbology( const QDomNode &layerNode, QString &errorMes QgsEditorWidgetSetup setup = QgsEditorWidgetSetup( widgetType, optionsMap ); mFieldWidgetSetups[fieldName] = setup; } + + // Legacy reading for QGIS 3.14 and older projects + // Attributes excluded from WMS and WFS + const QList> legacyConfig + { + qMakePair( QStringLiteral( "excludeAttributesWMS" ), QgsField::ConfigurationFlag::Wms ), + qMakePair( QStringLiteral( "excludeAttributesWFS" ), QgsField::ConfigurationFlag::Wfs ) + }; + for ( const auto &config : legacyConfig ) + { + QDomNode excludeNode = layerNode.namedItem( config.first ); + if ( !excludeNode.isNull() ) + { + QDomNodeList attributeNodeList = excludeNode.toElement().elementsByTagName( QStringLiteral( "attribute" ) ); + for ( int i = 0; i < attributeNodeList.size(); ++i ) + { + QString fieldName = attributeNodeList.at( i ).toElement().text(); + int index = mFields.indexFromName( fieldName ); + setFieldConfigurationFlag( index, config.second, false ); + } + } + } } if ( categories.testFlag( GeometryOptions ) ) @@ -5460,8 +5460,6 @@ void QgsVectorLayer::setFieldConfigurationFlags( int index, QgsField::Configurat if ( index < 0 || index >= mFields.count() ) return; - mFields.at( index ).setConfigurationFlags( flags ); - mFieldConfigurationFlags.insert( mFields.at( index ).name(), flags ); updateFields(); } diff --git a/src/gui/qgscheckablecombobox.cpp b/src/gui/qgscheckablecombobox.cpp index 3375c47142a4..8e286765b50c 100644 --- a/src/gui/qgscheckablecombobox.cpp +++ b/src/gui/qgscheckablecombobox.cpp @@ -138,7 +138,7 @@ void QgsCheckableComboBox::setDefaultText( const QString &text ) void QgsCheckableComboBox::addItemWithCheckState( const QString &text, Qt::CheckState state, const QVariant &userData ) { QComboBox::addItem( text, userData ); - setItemCheckState( count(), state ); + setItemCheckState( count() - 1, state ); } QStringList QgsCheckableComboBox::checkedItems() const diff --git a/src/gui/vector/qgssourcefieldsproperties.cpp b/src/gui/vector/qgssourcefieldsproperties.cpp index 8843c3dd5c19..d7a3c20961a6 100644 --- a/src/gui/vector/qgssourcefieldsproperties.cpp +++ b/src/gui/vector/qgssourcefieldsproperties.cpp @@ -65,15 +65,9 @@ QgsSourceFieldsProperties::QgsSourceFieldsProperties( QgsVectorLayer *layer, QWi mFieldsList->setHorizontalHeaderItem( AttrLengthCol, new QTableWidgetItem( tr( "Length" ) ) ); mFieldsList->setHorizontalHeaderItem( AttrPrecCol, new QTableWidgetItem( tr( "Precision" ) ) ); mFieldsList->setHorizontalHeaderItem( AttrCommentCol, new QTableWidgetItem( tr( "Comment" ) ) ); - const auto wmsWi = new QTableWidgetItem( QStringLiteral( "WMS" ) ); - wmsWi->setToolTip( tr( "Defines if this field is available in QGIS Server WMS service" ) ); - mFieldsList->setHorizontalHeaderItem( AttrWMSCol, wmsWi ); - const auto wfsWi = new QTableWidgetItem( QStringLiteral( "WFS" ) ); - wfsWi->setToolTip( tr( "Defines if this field is available in QGIS Server WFS (and OAPIF) service" ) ); - mFieldsList->setHorizontalHeaderItem( AttrWFSCol, wfsWi ); - const auto searchableWi = new QTableWidgetItem( QStringLiteral( "Searchable" ) ); - searchableWi->setToolTip( tr( "Defines if this field is searchable (active layer locator filter)" ) ); - mFieldsList->setHorizontalHeaderItem( AttrSearchableCol, searchableWi ); + const auto configurationFlagsWi = new QTableWidgetItem( QStringLiteral( "Configuration" ) ); + configurationFlagsWi->setToolTip( tr( "Configures the field" ) ); + mFieldsList->setHorizontalHeaderItem( AttrConfigurationFlagsCol, configurationFlagsWi ); mFieldsList->setHorizontalHeaderItem( AttrAliasCol, new QTableWidgetItem( tr( "Alias" ) ) ); mFieldsList->setSortingEnabled( true ); @@ -164,6 +158,9 @@ void QgsSourceFieldsProperties::attributeAdded( int idx ) for ( int i = 0; i < mFieldsList->columnCount(); i++ ) { + if ( i == AttrConfigurationFlagsCol ) + continue; + switch ( mLayer->fields().fieldOrigin( idx ) ) { case QgsFields::OriginExpression: @@ -275,18 +272,17 @@ void QgsSourceFieldsProperties::setRow( int row, int idx, const QgsField &field // Flags QgsCheckableComboBox *cb = new QgsCheckableComboBox( mFieldsList ); - const QMap flagList = qgsEnumMap(); - QMap::const_iterator flagIt; - for ( flagIt = flagList.constBegin(); flagIt != flagList.constEnd(); ++flagIt ) + const QList flagList = qgsEnumMap().keys(); + for ( const QgsField::ConfigurationFlag flag : flagList ) { - if ( flagIt.key() == QgsField::ConfigurationFlag::None || flagIt.key() == QgsField::ConfigurationFlag::DefaultFlags ) + if ( flag == QgsField::ConfigurationFlag::None || flag == QgsField::ConfigurationFlag::DefaultFlags ) continue; - cb->addItemWithCheckState( flagIt.value(), - mLayer->fieldConfigurationFlags( idx ).testFlag( flagIt.key() ) ? Qt::Unchecked : Qt::Checked, - QVariant::fromValue( flagIt.key() ) ); + cb->addItemWithCheckState( QgsField::readableConfigurationFlag( flag ), + mLayer->fieldConfigurationFlags( idx ).testFlag( flag ) ? Qt::Checked : Qt::Unchecked, + QVariant::fromValue( flag ) ); } - mFieldsList->setCellWidget( row, AttrSearchableCol, cb ); + mFieldsList->setCellWidget( row, AttrConfigurationFlagsCol, cb ); } bool QgsSourceFieldsProperties::addAttribute( const QgsField &field ) @@ -313,7 +309,7 @@ void QgsSourceFieldsProperties::apply() int idx = mFieldsList->item( i, AttrIdCol )->data( Qt::DisplayRole ).toInt(); QgsField::ConfigurationFlags flags = mLayer->fieldConfigurationFlags( idx ); - QgsCheckableComboBox *cb = qobject_cast( mFieldsList->cellWidget( i, AttrSearchableCol ) ); + QgsCheckableComboBox *cb = qobject_cast( mFieldsList->cellWidget( i, AttrConfigurationFlagsCol ) ); if ( cb ) { QgsCheckableItemModel *model = cb->model(); diff --git a/src/gui/vector/qgssourcefieldsproperties.h b/src/gui/vector/qgssourcefieldsproperties.h index a756f3ad140a..c204cbdfea4c 100644 --- a/src/gui/vector/qgssourcefieldsproperties.h +++ b/src/gui/vector/qgssourcefieldsproperties.h @@ -80,9 +80,7 @@ class GUI_EXPORT QgsSourceFieldsProperties : public QWidget, private Ui_QgsSourc AttrLengthCol, AttrPrecCol, AttrCommentCol, - AttrWMSCol, - AttrWFSCol, - AttrSearchableCol, + AttrConfigurationFlagsCol, AttrColCount, }; From b923c4a19f87b2b6335094fec095dfa895ada036 Mon Sep 17 00:00:00 2001 From: Denis Rouzaud Date: Mon, 14 Sep 2020 11:40:37 +0200 Subject: [PATCH 06/15] add deprecated dox --- python/core/auto_generated/qgsvectorlayer.sip.in | 8 ++++++++ src/core/qgsvectorlayer.h | 4 ++++ 2 files changed, 12 insertions(+) diff --git a/python/core/auto_generated/qgsvectorlayer.sip.in b/python/core/auto_generated/qgsvectorlayer.sip.in index 68d33b516c0b..1ecbddab4152 100644 --- a/python/core/auto_generated/qgsvectorlayer.sip.in +++ b/python/core/auto_generated/qgsvectorlayer.sip.in @@ -1971,21 +1971,29 @@ Returns a map of field name to attribute alias QSet excludeAttributesWms() /Deprecated/ const; %Docstring A set of attributes that are not advertised in WMS requests with QGIS server. + +.. deprecated:: QGIS 3.16 %End void setExcludeAttributesWms( const QSet &att ) /Deprecated/; %Docstring A set of attributes that are not advertised in WMS requests with QGIS server. + +.. deprecated:: QGIS 3.16 %End QSet excludeAttributesWfs() /Deprecated/ const; %Docstring A set of attributes that are not advertised in WFS requests with QGIS server. + +.. deprecated:: QGIS 3.16 %End void setExcludeAttributesWfs( const QSet &att ) /Deprecated/; %Docstring A set of attributes that are not advertised in WFS requests with QGIS server. + +.. deprecated:: QGIS 3.16 %End virtual bool deleteAttribute( int attr ); diff --git a/src/core/qgsvectorlayer.h b/src/core/qgsvectorlayer.h index 3e31bfd06cf9..ea3bf5a8681c 100644 --- a/src/core/qgsvectorlayer.h +++ b/src/core/qgsvectorlayer.h @@ -1839,21 +1839,25 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte /** * A set of attributes that are not advertised in WMS requests with QGIS server. + * \deprecated since QGIS 3.16 */ Q_DECL_DEPRECATED QSet excludeAttributesWms() SIP_DEPRECATED const { return mExcludeAttributesWMS; } /** * A set of attributes that are not advertised in WMS requests with QGIS server. + * \deprecated since QGIS 3.16 */ Q_DECL_DEPRECATED void setExcludeAttributesWms( const QSet &att ) SIP_DEPRECATED { mExcludeAttributesWMS = att; } /** * A set of attributes that are not advertised in WFS requests with QGIS server. + * \deprecated since QGIS 3.16 */ Q_DECL_DEPRECATED QSet excludeAttributesWfs() SIP_DEPRECATED const { return mExcludeAttributesWFS; } /** * A set of attributes that are not advertised in WFS requests with QGIS server. + * \deprecated since QGIS 3.16 */ Q_DECL_DEPRECATED void setExcludeAttributesWfs( const QSet &att ) SIP_DEPRECATED { mExcludeAttributesWFS = att; } From 9cf85dd1fe55d35b24bda4c06beede95b2906813 Mon Sep 17 00:00:00 2001 From: Denis Rouzaud Date: Tue, 15 Sep 2020 08:42:19 +0200 Subject: [PATCH 07/15] better enum names, better dox Co-authored-by: Nyall Dawson --- python/core/auto_generated/qgsfield.sip.in | 2 +- src/core/qgsfield.cpp | 4 ++-- src/core/qgsfield.h | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/python/core/auto_generated/qgsfield.sip.in b/python/core/auto_generated/qgsfield.sip.in index c903d2d97795..265301e6c38a 100644 --- a/python/core/auto_generated/qgsfield.sip.in +++ b/python/core/auto_generated/qgsfield.sip.in @@ -293,7 +293,7 @@ Formats string for display static QString readableConfigurationFlag( ConfigurationFlag flag ); %Docstring -Returns the reabable and translated value of the configuration flag +Returns a readable and translated value of the configuration flag .. versionadded:: 3.16 %End diff --git a/src/core/qgsfield.cpp b/src/core/qgsfield.cpp index 8f2c3fea218b..e7320424d889 100644 --- a/src/core/qgsfield.cpp +++ b/src/core/qgsfield.cpp @@ -355,9 +355,9 @@ QString QgsField::readableConfigurationFlag( QgsField::ConfigurationFlag flag ) case ConfigurationFlag::Searchable: return QObject::tr( "Searchable" ); case ConfigurationFlag::Wms: - return QStringLiteral( "WMS" ); + return QStringLiteral( "Expose via WMS" ); case ConfigurationFlag::Wfs: - return QStringLiteral( "WFS" ); + return QStringLiteral( "Expose via WFS" ); case ConfigurationFlag::DefaultFlags: return QObject::tr( "Default flags" ); } diff --git a/src/core/qgsfield.h b/src/core/qgsfield.h index 1c1041eee92a..a0bdf900cf01 100644 --- a/src/core/qgsfield.h +++ b/src/core/qgsfield.h @@ -81,8 +81,8 @@ class CORE_EXPORT QgsField { None = 0, //!< No flag is defined Searchable = 1 << 1, //!< Defines if the field is searchable (used in the locator search for instance) - Wms = 1 << 2, //!< Fields is available if layer is served as WMS - Wfs = 1 << 3, //!< Fields is available if layer is served as WFS + ExposeViaWms = 1 << 2, //!< Fields is available if layer is served as WMS from QGIS server + ExposeViaWfs = 1 << 3, //!< Fields is available if layer is served as WFS from QGIS server DefaultFlags = Searchable | Wms | Wfs, //!< Default set of flags for a field }; Q_ENUM( ConfigurationFlag ) From 81064f6ac1b23dcf0031a102564d9264bbba4bdc Mon Sep 17 00:00:00 2001 From: Denis Rouzaud Date: Tue, 15 Sep 2020 08:40:28 +0200 Subject: [PATCH 08/15] remove deprecated usage in server --- .../landingpage/qgslandingpageutils.cpp | 26 +++++++++---------- .../wfs/qgswfsdescribefeaturetype.cpp | 3 +-- src/server/services/wfs3/qgswfs3handlers.cpp | 7 +++-- .../services/wms/qgswmsgetcapabilities.cpp | 3 +-- src/server/services/wms/qgswmsrenderer.cpp | 5 ++-- 5 files changed, 20 insertions(+), 24 deletions(-) diff --git a/src/server/services/landingpage/qgslandingpageutils.cpp b/src/server/services/landingpage/qgslandingpageutils.cpp index a58b39cb541c..9981aaf115b0 100644 --- a/src/server/services/landingpage/qgslandingpageutils.cpp +++ b/src/server/services/landingpage/qgslandingpageutils.cpp @@ -453,34 +453,34 @@ json QgsLandingPageUtils::projectInfo( const QString &projectUri ) wmsLayer[ "pk" ] = vl->primaryKeyAttributes(); int fieldIdx { 0 }; json fieldsData; - const auto &cFields { vl->fields() }; - for ( const auto &f : cFields ) + const QgsFields &cFields { vl->fields() }; + for ( const QgsField &field : cFields ) { - if ( vl->excludeAttributesWfs().contains( vl->name() ) ) + if ( !field.configurationFlags().testFlag( QgsField::ConfigurationFlag::Wfs ) ) { ++fieldIdx; continue; } - const auto &constraints { f.constraints().constraints() }; + const QgsFieldConstraints::Constraints constraints { field.constraints().constraints() }; const bool notNull { constraints &QgsFieldConstraints::Constraint::ConstraintNotNull && - f.constraints().constraintStrength( QgsFieldConstraints::Constraint::ConstraintNotNull ) == QgsFieldConstraints::ConstraintStrength::ConstraintStrengthHard }; + field.constraints().constraintStrength( QgsFieldConstraints::Constraint::ConstraintNotNull ) == QgsFieldConstraints::ConstraintStrength::ConstraintStrengthHard }; const bool unique { constraints &QgsFieldConstraints::Constraint::ConstraintUnique && - f.constraints().constraintStrength( QgsFieldConstraints::Constraint::ConstraintUnique ) == QgsFieldConstraints::ConstraintStrength::ConstraintStrengthHard }; + field.constraints().constraintStrength( QgsFieldConstraints::Constraint::ConstraintUnique ) == QgsFieldConstraints::ConstraintStrength::ConstraintStrengthHard }; const bool hasExpression { constraints &QgsFieldConstraints::Constraint::ConstraintExpression && - f.constraints().constraintStrength( QgsFieldConstraints::Constraint::ConstraintExpression ) == QgsFieldConstraints::ConstraintStrength::ConstraintStrengthHard }; + field.constraints().constraintStrength( QgsFieldConstraints::Constraint::ConstraintExpression ) == QgsFieldConstraints::ConstraintStrength::ConstraintStrengthHard }; const QString &defaultValue { vl->dataProvider()->defaultValueClause( fieldIdx ) }; const bool isReadOnly( notNull && unique && ! defaultValue.isEmpty() ); - fieldsData[ f.name().toStdString() ] = + fieldsData[ field.name().toStdString() ] = { - { "type", f.typeName().toStdString() }, - { "label", f.alias().isEmpty() ? f.name().toStdString() : f.alias().toStdString() }, - { "precision", f.precision() }, - { "length", f.length() }, + { "type", field.typeName().toStdString() }, + { "label", field.alias().isEmpty() ? field.name().toStdString() : field.alias().toStdString() }, + { "precision", field.precision() }, + { "length", field.length() }, { "unique", unique }, { "not_null", notNull }, { "has_expression", hasExpression }, { "default", defaultValue.toStdString() }, - { "expression", f.constraints().constraintExpression().toStdString() }, + { "expression", field.constraints().constraintExpression().toStdString() }, { "editable", !isReadOnly } }; diff --git a/src/server/services/wfs/qgswfsdescribefeaturetype.cpp b/src/server/services/wfs/qgswfsdescribefeaturetype.cpp index 47d0f167013e..141e03989ace 100644 --- a/src/server/services/wfs/qgswfsdescribefeaturetype.cpp +++ b/src/server/services/wfs/qgswfsdescribefeaturetype.cpp @@ -262,13 +262,12 @@ namespace QgsWfs //Attributes QgsFields fields = layer->fields(); //hidden attributes for this layer - const QSet &layerExcludedAttributes = layer->excludeAttributesWfs(); for ( int idx = 0; idx < fields.count(); ++idx ) { const QgsField field = fields.at( idx ); QString attributeName = field.name(); //skip attribute if excluded from WFS publication - if ( layerExcludedAttributes.contains( attributeName ) ) + if ( !field.configurationFlags().testFlag( QgsField::ConfigurationFlag::Wfs ) ) { continue; } diff --git a/src/server/services/wfs3/qgswfs3handlers.cpp b/src/server/services/wfs3/qgswfs3handlers.cpp index 0ab43672b0bf..179d2e84e05d 100644 --- a/src/server/services/wfs3/qgswfs3handlers.cpp +++ b/src/server/services/wfs3/qgswfs3handlers.cpp @@ -242,13 +242,12 @@ QgsFields QgsWfs3AbstractItemsHandler::publishedFields( const QgsVectorLayer *vL QStringList publishedAttributes = QStringList(); // Removed attributes // WFS excluded attributes for this layer - const QSet &layerExcludedAttributes = vLayer->excludeAttributesWfs(); const QgsFields &fields = vLayer->fields(); - for ( int i = 0; i < fields.count(); ++i ) + for ( const QgsField &field : fields ) { - if ( ! layerExcludedAttributes.contains( fields.at( i ).name() ) ) + if ( field.configurationFlags().testFlag( QgsField::ConfigurationFlag::Wfs ) ) { - publishedAttributes.push_back( fields.at( i ).name() ); + publishedAttributes.push_back( field.name() ); } } diff --git a/src/server/services/wms/qgswmsgetcapabilities.cpp b/src/server/services/wms/qgswmsgetcapabilities.cpp index 2f49f2accc28..a402c435f897 100644 --- a/src/server/services/wms/qgswmsgetcapabilities.cpp +++ b/src/server/services/wms/qgswmsgetcapabilities.cpp @@ -1862,7 +1862,6 @@ namespace QgsWms case QgsMapLayerType::VectorLayer: { QgsVectorLayer *vLayer = static_cast( currentLayer ); - const QSet &excludedAttributes = vLayer->excludeAttributesWms(); int displayFieldIdx = -1; QString displayField = QStringLiteral( "maptip" ); @@ -1879,7 +1878,7 @@ namespace QgsWms for ( int idx = 0; idx < layerFields.count(); ++idx ) { QgsField field = layerFields.at( idx ); - if ( excludedAttributes.contains( field.name() ) ) + if ( !field.configurationFlags().testFlag( QgsField::ConfigurationFlag::Wms ) ) { continue; } diff --git a/src/server/services/wms/qgswmsrenderer.cpp b/src/server/services/wms/qgswmsrenderer.cpp index eb593018be57..09ea38c62694 100644 --- a/src/server/services/wms/qgswmsrenderer.cpp +++ b/src/server/services/wms/qgswmsrenderer.cpp @@ -1447,7 +1447,6 @@ namespace QgsWms const QgsFields fields = layer->fields(); bool addWktGeometry = ( QgsServerProjectUtils::wmsFeatureInfoAddWktGeometry( *mProject ) && mWmsParameters.withGeometry() ); bool segmentizeWktGeometry = QgsServerProjectUtils::wmsFeatureInfoSegmentizeWktGeometry( *mProject ); - const QSet &excludedAttributes = layer->excludeAttributesWms(); bool hasGeometry = QgsServerProjectUtils::wmsFeatureInfoAddWktGeometry( *mProject ) || addWktGeometry || featureBBox || layerFilterGeom; fReq.setFlags( ( ( hasGeometry ) ? QgsFeatureRequest::NoFlags : QgsFeatureRequest::NoGeometry ) | QgsFeatureRequest::ExactIntersect ); @@ -1568,7 +1567,7 @@ namespace QgsWms for ( int i = 0; i < featureAttributes.count(); ++i ) { //skip attribute if it is explicitly excluded from WMS publication - if ( excludedAttributes.contains( fields.at( i ).name() ) ) + if ( fields.at( i ).configurationFlags().testFlag( QgsField::ConfigurationFlag::Wms ) ) { continue; } @@ -2417,7 +2416,7 @@ namespace QgsWms { QString attributeName = fields.at( i ).name(); //skip attribute if it is explicitly excluded from WMS publication - if ( layer && layer->excludeAttributesWms().contains( attributeName ) ) + if ( !fields.at( i ).configurationFlags().testFlag( QgsField::ConfigurationFlag::Wms ) ) { continue; } From 1660d726a46a1265bdde3feede0f84a9722a4649 Mon Sep 17 00:00:00 2001 From: Denis Rouzaud Date: Tue, 15 Sep 2020 08:44:51 +0200 Subject: [PATCH 09/15] add deprecation details --- python/core/auto_generated/qgsvectorlayer.sip.in | 4 ++++ src/core/qgsvectorlayer.cpp | 4 ++-- src/core/qgsvectorlayer.h | 8 ++++---- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/python/core/auto_generated/qgsvectorlayer.sip.in b/python/core/auto_generated/qgsvectorlayer.sip.in index 1ecbddab4152..e15af00ae8f0 100644 --- a/python/core/auto_generated/qgsvectorlayer.sip.in +++ b/python/core/auto_generated/qgsvectorlayer.sip.in @@ -1973,6 +1973,7 @@ Returns a map of field name to attribute alias A set of attributes that are not advertised in WMS requests with QGIS server. .. deprecated:: QGIS 3.16 + use fields().configurationFlags() instead %End void setExcludeAttributesWms( const QSet &att ) /Deprecated/; @@ -1980,6 +1981,7 @@ A set of attributes that are not advertised in WMS requests with QGIS server. A set of attributes that are not advertised in WMS requests with QGIS server. .. deprecated:: QGIS 3.16 + use setFieldConfigurationFlag instead %End QSet excludeAttributesWfs() /Deprecated/ const; @@ -1987,6 +1989,7 @@ A set of attributes that are not advertised in WMS requests with QGIS server. A set of attributes that are not advertised in WFS requests with QGIS server. .. deprecated:: QGIS 3.16 + use fields().configurationFlags() instead %End void setExcludeAttributesWfs( const QSet &att ) /Deprecated/; @@ -1994,6 +1997,7 @@ A set of attributes that are not advertised in WFS requests with QGIS server. A set of attributes that are not advertised in WFS requests with QGIS server. .. deprecated:: QGIS 3.16 + use setFieldConfigurationFlag instead %End virtual bool deleteAttribute( int attr ); diff --git a/src/core/qgsvectorlayer.cpp b/src/core/qgsvectorlayer.cpp index 0a0f9873718d..2c26ddcb91bf 100644 --- a/src/core/qgsvectorlayer.cpp +++ b/src/core/qgsvectorlayer.cpp @@ -2331,8 +2331,8 @@ bool QgsVectorLayer::readSymbology( const QDomNode &layerNode, QString &errorMes // Attributes excluded from WMS and WFS const QList> legacyConfig { - qMakePair( QStringLiteral( "excludeAttributesWMS" ), QgsField::ConfigurationFlag::Wms ), - qMakePair( QStringLiteral( "excludeAttributesWFS" ), QgsField::ConfigurationFlag::Wfs ) + qMakePair( QStringLiteral( "excludeAttributesWMS" ), QgsField::ConfigurationFlag::ExposeViaWms ), + qMakePair( QStringLiteral( "excludeAttributesWFS" ), QgsField::ConfigurationFlag::ExposeViaWfs ) }; for ( const auto &config : legacyConfig ) { diff --git a/src/core/qgsvectorlayer.h b/src/core/qgsvectorlayer.h index ea3bf5a8681c..65542b267fee 100644 --- a/src/core/qgsvectorlayer.h +++ b/src/core/qgsvectorlayer.h @@ -1839,25 +1839,25 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte /** * A set of attributes that are not advertised in WMS requests with QGIS server. - * \deprecated since QGIS 3.16 + * \deprecated since QGIS 3.16, use fields().configurationFlags() instead */ Q_DECL_DEPRECATED QSet excludeAttributesWms() SIP_DEPRECATED const { return mExcludeAttributesWMS; } /** * A set of attributes that are not advertised in WMS requests with QGIS server. - * \deprecated since QGIS 3.16 + * \deprecated since QGIS 3.16, use setFieldConfigurationFlag instead */ Q_DECL_DEPRECATED void setExcludeAttributesWms( const QSet &att ) SIP_DEPRECATED { mExcludeAttributesWMS = att; } /** * A set of attributes that are not advertised in WFS requests with QGIS server. - * \deprecated since QGIS 3.16 + * \deprecated since QGIS 3.16, use fields().configurationFlags() instead */ Q_DECL_DEPRECATED QSet excludeAttributesWfs() SIP_DEPRECATED const { return mExcludeAttributesWFS; } /** * A set of attributes that are not advertised in WFS requests with QGIS server. - * \deprecated since QGIS 3.16 + * \deprecated since QGIS 3.16, use setFieldConfigurationFlag instead */ Q_DECL_DEPRECATED void setExcludeAttributesWfs( const QSet &att ) SIP_DEPRECATED { mExcludeAttributesWFS = att; } From fb7f7ba6971a39f5095aebd1d043400379e5d855 Mon Sep 17 00:00:00 2001 From: Denis Rouzaud Date: Tue, 15 Sep 2020 08:45:20 +0200 Subject: [PATCH 10/15] follow up renaming enum keys --- python/core/auto_generated/qgsfield.sip.in | 2 +- src/core/qgsfield.cpp | 4 ++-- src/core/qgsfield.h | 2 +- .../landingpage/qgslandingpageutils.cpp | 2 +- .../wfs/qgswfsdescribefeaturetype.cpp | 2 +- src/server/services/wfs/qgswfsgetfeature.cpp | 20 ++++--------------- src/server/services/wfs3/qgswfs3handlers.cpp | 2 +- .../services/wms/qgswmsgetcapabilities.cpp | 2 +- src/server/services/wms/qgswmsrenderer.cpp | 4 ++-- 9 files changed, 14 insertions(+), 26 deletions(-) diff --git a/python/core/auto_generated/qgsfield.sip.in b/python/core/auto_generated/qgsfield.sip.in index 265301e6c38a..c903d2d97795 100644 --- a/python/core/auto_generated/qgsfield.sip.in +++ b/python/core/auto_generated/qgsfield.sip.in @@ -293,7 +293,7 @@ Formats string for display static QString readableConfigurationFlag( ConfigurationFlag flag ); %Docstring -Returns a readable and translated value of the configuration flag +Returns the reabable and translated value of the configuration flag .. versionadded:: 3.16 %End diff --git a/src/core/qgsfield.cpp b/src/core/qgsfield.cpp index e7320424d889..9cc7a4e9cbf0 100644 --- a/src/core/qgsfield.cpp +++ b/src/core/qgsfield.cpp @@ -354,9 +354,9 @@ QString QgsField::readableConfigurationFlag( QgsField::ConfigurationFlag flag ) return QObject::tr( "None" ); case ConfigurationFlag::Searchable: return QObject::tr( "Searchable" ); - case ConfigurationFlag::Wms: + case ConfigurationFlag::ExposeViaWms: return QStringLiteral( "Expose via WMS" ); - case ConfigurationFlag::Wfs: + case ConfigurationFlag::ExposeViaWfs: return QStringLiteral( "Expose via WFS" ); case ConfigurationFlag::DefaultFlags: return QObject::tr( "Default flags" ); diff --git a/src/core/qgsfield.h b/src/core/qgsfield.h index a0bdf900cf01..bea2c6d29c98 100644 --- a/src/core/qgsfield.h +++ b/src/core/qgsfield.h @@ -83,7 +83,7 @@ class CORE_EXPORT QgsField Searchable = 1 << 1, //!< Defines if the field is searchable (used in the locator search for instance) ExposeViaWms = 1 << 2, //!< Fields is available if layer is served as WMS from QGIS server ExposeViaWfs = 1 << 3, //!< Fields is available if layer is served as WFS from QGIS server - DefaultFlags = Searchable | Wms | Wfs, //!< Default set of flags for a field + DefaultFlags = Searchable | ExposeViaWms | ExposeViaWfs, //!< Default set of flags for a field }; Q_ENUM( ConfigurationFlag ) Q_DECLARE_FLAGS( ConfigurationFlags, ConfigurationFlag ) diff --git a/src/server/services/landingpage/qgslandingpageutils.cpp b/src/server/services/landingpage/qgslandingpageutils.cpp index 9981aaf115b0..450f00c6afa2 100644 --- a/src/server/services/landingpage/qgslandingpageutils.cpp +++ b/src/server/services/landingpage/qgslandingpageutils.cpp @@ -456,7 +456,7 @@ json QgsLandingPageUtils::projectInfo( const QString &projectUri ) const QgsFields &cFields { vl->fields() }; for ( const QgsField &field : cFields ) { - if ( !field.configurationFlags().testFlag( QgsField::ConfigurationFlag::Wfs ) ) + if ( !field.configurationFlags().testFlag( QgsField::ConfigurationFlag::ExposeViaWfs ) ) { ++fieldIdx; continue; diff --git a/src/server/services/wfs/qgswfsdescribefeaturetype.cpp b/src/server/services/wfs/qgswfsdescribefeaturetype.cpp index 141e03989ace..1870f509260f 100644 --- a/src/server/services/wfs/qgswfsdescribefeaturetype.cpp +++ b/src/server/services/wfs/qgswfsdescribefeaturetype.cpp @@ -267,7 +267,7 @@ namespace QgsWfs const QgsField field = fields.at( idx ); QString attributeName = field.name(); //skip attribute if excluded from WFS publication - if ( !field.configurationFlags().testFlag( QgsField::ConfigurationFlag::Wfs ) ) + if ( !field.configurationFlags().testFlag( QgsField::ConfigurationFlag::ExposeViaWfs ) ) { continue; } diff --git a/src/server/services/wfs/qgswfsgetfeature.cpp b/src/server/services/wfs/qgswfsgetfeature.cpp index 54a3adbbc02d..973548bb8780 100644 --- a/src/server/services/wfs/qgswfsgetfeature.cpp +++ b/src/server/services/wfs/qgswfsgetfeature.cpp @@ -249,7 +249,7 @@ namespace QgsWfs //Using pending attributes and pending fields QgsAttributeList attrIndexes = vlayer->attributeList(); - QgsFields fields = vlayer->fields(); + const QgsFields fields = vlayer->fields(); bool withGeom = true; if ( !propertyList.isEmpty() && propertyList.first() != QStringLiteral( "*" ) ) { @@ -261,6 +261,9 @@ namespace QgsWfs QList fieldnames; for ( int idx = 0; idx < fields.count(); ++idx ) { + if (!fields.at(idx).configurationFlags().testFlag(QgsField::ConfigurationFlag::ExposeViaWfs)) + continue; + fieldnames.append( fields[idx].name() ); propertynames.append( fields.field( idx ).name().replace( ' ', '_' ).replace( cleanTagNameRegExp, QString() ) ); } @@ -288,21 +291,6 @@ namespace QgsWfs } } - //excluded attributes for this layer - const QSet &layerExcludedAttributes = vlayer->excludeAttributesWfs(); - if ( !attrIndexes.isEmpty() && !layerExcludedAttributes.isEmpty() ) - { - foreach ( const QString &excludedAttribute, layerExcludedAttributes ) - { - int fieldNameIdx = fields.indexOf( excludedAttribute ); - if ( fieldNameIdx > -1 && attrIndexes.contains( fieldNameIdx ) ) - { - attrIndexes.removeOne( fieldNameIdx ); - } - } - } - - // update request QgsFeatureRequest featureRequest = query.featureRequest; diff --git a/src/server/services/wfs3/qgswfs3handlers.cpp b/src/server/services/wfs3/qgswfs3handlers.cpp index 179d2e84e05d..628102d5fa66 100644 --- a/src/server/services/wfs3/qgswfs3handlers.cpp +++ b/src/server/services/wfs3/qgswfs3handlers.cpp @@ -245,7 +245,7 @@ QgsFields QgsWfs3AbstractItemsHandler::publishedFields( const QgsVectorLayer *vL const QgsFields &fields = vLayer->fields(); for ( const QgsField &field : fields ) { - if ( field.configurationFlags().testFlag( QgsField::ConfigurationFlag::Wfs ) ) + if ( field.configurationFlags().testFlag( QgsField::ConfigurationFlag::ExposeViaWfs ) ) { publishedAttributes.push_back( field.name() ); } diff --git a/src/server/services/wms/qgswmsgetcapabilities.cpp b/src/server/services/wms/qgswmsgetcapabilities.cpp index a402c435f897..938c891b25c3 100644 --- a/src/server/services/wms/qgswmsgetcapabilities.cpp +++ b/src/server/services/wms/qgswmsgetcapabilities.cpp @@ -1878,7 +1878,7 @@ namespace QgsWms for ( int idx = 0; idx < layerFields.count(); ++idx ) { QgsField field = layerFields.at( idx ); - if ( !field.configurationFlags().testFlag( QgsField::ConfigurationFlag::Wms ) ) + if ( !field.configurationFlags().testFlag( QgsField::ConfigurationFlag::ExposeViaWms ) ) { continue; } diff --git a/src/server/services/wms/qgswmsrenderer.cpp b/src/server/services/wms/qgswmsrenderer.cpp index 09ea38c62694..ca26a7abfe4d 100644 --- a/src/server/services/wms/qgswmsrenderer.cpp +++ b/src/server/services/wms/qgswmsrenderer.cpp @@ -1567,7 +1567,7 @@ namespace QgsWms for ( int i = 0; i < featureAttributes.count(); ++i ) { //skip attribute if it is explicitly excluded from WMS publication - if ( fields.at( i ).configurationFlags().testFlag( QgsField::ConfigurationFlag::Wms ) ) + if ( fields.at( i ).configurationFlags().testFlag( QgsField::ConfigurationFlag::ExposeViaWms ) ) { continue; } @@ -2416,7 +2416,7 @@ namespace QgsWms { QString attributeName = fields.at( i ).name(); //skip attribute if it is explicitly excluded from WMS publication - if ( !fields.at( i ).configurationFlags().testFlag( QgsField::ConfigurationFlag::Wms ) ) + if ( !fields.at( i ).configurationFlags().testFlag( QgsField::ConfigurationFlag::ExposeViaWms ) ) { continue; } From 7265d9de96cc388a377a29d87d14899b0ce21170 Mon Sep 17 00:00:00 2001 From: Denis Rouzaud Date: Tue, 15 Sep 2020 09:50:54 +0200 Subject: [PATCH 11/15] fix sip --- python/core/auto_generated/qgsfield.sip.in | 6 ------ python/core/auto_generated/qgsvectorlayer.sip.in | 4 ++-- python/gui/auto_generated/qgscheckablecombobox.sip.in | 8 -------- src/core/qgsfield.h | 2 +- src/core/qgsvectorlayer.h | 4 ++-- src/gui/qgscheckablecombobox.h | 2 +- src/server/services/wfs/qgswfsgetfeature.cpp | 2 +- 7 files changed, 7 insertions(+), 21 deletions(-) diff --git a/python/core/auto_generated/qgsfield.sip.in b/python/core/auto_generated/qgsfield.sip.in index c903d2d97795..a422d847b677 100644 --- a/python/core/auto_generated/qgsfield.sip.in +++ b/python/core/auto_generated/qgsfield.sip.in @@ -291,12 +291,6 @@ Sets the alias for the field (the friendly displayed name of the field ). Formats string for display %End - static QString readableConfigurationFlag( ConfigurationFlag flag ); -%Docstring -Returns the reabable and translated value of the configuration flag - -.. versionadded:: 3.16 -%End bool convertCompatible( QVariant &v ) const; diff --git a/python/core/auto_generated/qgsvectorlayer.sip.in b/python/core/auto_generated/qgsvectorlayer.sip.in index e15af00ae8f0..cb0eee31d26c 100644 --- a/python/core/auto_generated/qgsvectorlayer.sip.in +++ b/python/core/auto_generated/qgsvectorlayer.sip.in @@ -1968,7 +1968,7 @@ Convenience function that returns the attribute alias if defined or the field na Returns a map of field name to attribute alias %End - QSet excludeAttributesWms() /Deprecated/ const; + QSet excludeAttributesWms() const /Deprecated/; %Docstring A set of attributes that are not advertised in WMS requests with QGIS server. @@ -1984,7 +1984,7 @@ A set of attributes that are not advertised in WMS requests with QGIS server. use setFieldConfigurationFlag instead %End - QSet excludeAttributesWfs() /Deprecated/ const; + QSet excludeAttributesWfs() const /Deprecated/; %Docstring A set of attributes that are not advertised in WFS requests with QGIS server. diff --git a/python/gui/auto_generated/qgscheckablecombobox.sip.in b/python/gui/auto_generated/qgscheckablecombobox.sip.in index c2c8b876d229..83c99a6671aa 100644 --- a/python/gui/auto_generated/qgscheckablecombobox.sip.in +++ b/python/gui/auto_generated/qgscheckablecombobox.sip.in @@ -122,14 +122,6 @@ Toggles the item check state .. seealso:: :py:func:`setItemCheckState` %End - QgsCheckableItemModel *model() const; -%Docstring -Returns the custom item model which handles checking the items - -.. seealso:: :py:class:`QgsCheckableItemModel` - -.. versionadded:: 3.16 -%End virtual void hidePopup(); diff --git a/src/core/qgsfield.h b/src/core/qgsfield.h index bea2c6d29c98..a53038e468b2 100644 --- a/src/core/qgsfield.h +++ b/src/core/qgsfield.h @@ -333,7 +333,7 @@ class CORE_EXPORT QgsField * Returns the reabable and translated value of the configuration flag * \since QGIS 3.16 */ - static QString readableConfigurationFlag( ConfigurationFlag flag ); + static QString readableConfigurationFlag( QgsField::ConfigurationFlag flag ) SIP_SKIP; #ifndef SIP_RUN diff --git a/src/core/qgsvectorlayer.h b/src/core/qgsvectorlayer.h index 65542b267fee..7ed91f01eef2 100644 --- a/src/core/qgsvectorlayer.h +++ b/src/core/qgsvectorlayer.h @@ -1841,7 +1841,7 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte * A set of attributes that are not advertised in WMS requests with QGIS server. * \deprecated since QGIS 3.16, use fields().configurationFlags() instead */ - Q_DECL_DEPRECATED QSet excludeAttributesWms() SIP_DEPRECATED const { return mExcludeAttributesWMS; } + Q_DECL_DEPRECATED QSet excludeAttributesWms() const SIP_DEPRECATED { return mExcludeAttributesWMS; } /** * A set of attributes that are not advertised in WMS requests with QGIS server. @@ -1853,7 +1853,7 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte * A set of attributes that are not advertised in WFS requests with QGIS server. * \deprecated since QGIS 3.16, use fields().configurationFlags() instead */ - Q_DECL_DEPRECATED QSet excludeAttributesWfs() SIP_DEPRECATED const { return mExcludeAttributesWFS; } + Q_DECL_DEPRECATED QSet excludeAttributesWfs() const SIP_DEPRECATED { return mExcludeAttributesWFS; } /** * A set of attributes that are not advertised in WFS requests with QGIS server. diff --git a/src/gui/qgscheckablecombobox.h b/src/gui/qgscheckablecombobox.h index 991f34894c10..9ed7097d5a6a 100644 --- a/src/gui/qgscheckablecombobox.h +++ b/src/gui/qgscheckablecombobox.h @@ -214,7 +214,7 @@ class GUI_EXPORT QgsCheckableComboBox : public QComboBox * \see QgsCheckableItemModel * \since QGIS 3.16 */ - QgsCheckableItemModel *model() const {return mModel;} + QgsCheckableItemModel *model() const SIP_SKIP {return mModel;} /** * Hides the list of items in the combobox if it is currently diff --git a/src/server/services/wfs/qgswfsgetfeature.cpp b/src/server/services/wfs/qgswfsgetfeature.cpp index 973548bb8780..7033160c75ab 100644 --- a/src/server/services/wfs/qgswfsgetfeature.cpp +++ b/src/server/services/wfs/qgswfsgetfeature.cpp @@ -261,7 +261,7 @@ namespace QgsWfs QList fieldnames; for ( int idx = 0; idx < fields.count(); ++idx ) { - if (!fields.at(idx).configurationFlags().testFlag(QgsField::ConfigurationFlag::ExposeViaWfs)) + if ( !fields.at( idx ).configurationFlags().testFlag( QgsField::ConfigurationFlag::ExposeViaWfs ) ) continue; fieldnames.append( fields[idx].name() ); From 4afe021f645a7e7c8081cc98d83a0b5b38677f64 Mon Sep 17 00:00:00 2001 From: Denis Rouzaud Date: Tue, 15 Sep 2020 14:56:56 +0200 Subject: [PATCH 12/15] fix msissing negation --- src/server/services/wms/qgswmsrenderer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/services/wms/qgswmsrenderer.cpp b/src/server/services/wms/qgswmsrenderer.cpp index ca26a7abfe4d..2a5b7418894d 100644 --- a/src/server/services/wms/qgswmsrenderer.cpp +++ b/src/server/services/wms/qgswmsrenderer.cpp @@ -1567,7 +1567,7 @@ namespace QgsWms for ( int i = 0; i < featureAttributes.count(); ++i ) { //skip attribute if it is explicitly excluded from WMS publication - if ( fields.at( i ).configurationFlags().testFlag( QgsField::ConfigurationFlag::ExposeViaWms ) ) + if ( !fields.at( i ).configurationFlags().testFlag( QgsField::ConfigurationFlag::ExposeViaWms ) ) { continue; } From c738609d9177ed398ba2a0ae0e1c16448accd5f6 Mon Sep 17 00:00:00 2001 From: Denis Rouzaud Date: Tue, 15 Sep 2020 16:26:40 +0200 Subject: [PATCH 13/15] update test projects (open and save) --- .../landingpage/projects/Project1.qgs | 175 +- .../landingpage/projects/Project2.qgz | Bin 8183 -> 8595 bytes .../landingpage/projects/project_data.gpkg | Bin 118784 -> 118784 bytes .../test_project_wms_grouped_layers.gpkg | Bin 692224 -> 692224 bytes ...test_project_wms_grouped_nested_layers.qgs | 2066 +++++++++-------- .../landingpage/projects2/project3.qgz | Bin 7167 -> 7763 bytes 6 files changed, 1173 insertions(+), 1068 deletions(-) diff --git a/tests/testdata/qgis_server/landingpage/projects/Project1.qgs b/tests/testdata/qgis_server/landingpage/projects/Project1.qgs index 1272a1657dd0..8fb556c74bad 100644 --- a/tests/testdata/qgis_server/landingpage/projects/Project1.qgs +++ b/tests/testdata/qgis_server/landingpage/projects/Project1.qgs @@ -1,5 +1,5 @@ - + Project1 Title @@ -20,20 +20,20 @@ - + points_842425df_7f45_4091_a6c9_086e1dc1edd1 - + - + - + degrees -23.31087998257129357 @@ -58,17 +58,65 @@ 0 - - - + + + Annotations_e424930a_3838_4a42_94ca_e28ac525201f + + + + + + + + + + 0 + 0 + + + + + false + + + + + + + + + + + + + + + + + 0 + 0 + + + + + false + + + + + + 1 + - + -1.12365841865539995 43.23918533325200286 @@ -85,7 +133,7 @@ http://data.url Qgis Server Layer Properties Attribution - http://metadata.url + http://metadata.url points @@ -129,7 +177,7 @@ distributor - + Once upon a time ... None @@ -151,7 +199,7 @@ - + @@ -175,10 +223,16 @@ 1 1 - + + + + + + + - - + + @@ -199,9 +253,9 @@ @@ -219,16 +273,20 @@ 0 1 - + - + - - + + + + + + @@ -239,13 +297,14 @@ + @@ -253,12 +312,12 @@ - + @@ -267,16 +326,15 @@ - - + - + - - + + - - - - + + - - + + - + - @@ -342,8 +398,8 @@ def my_form_open(dialog, layer, feature): 0 generatedlayout - - + + @@ -485,9 +541,16 @@ def my_form_open(dialog, layer, feature): + + + - + PROJCRS["Amersfoort / RD New",BASEGEOGCRS["Amersfoort",DATUM["Amersfoort",ELLIPSOID["Bessel 1841",6377397.155,299.1528128,LENGTHUNIT["metre",1]]],PRIMEM["Greenwich",0,ANGLEUNIT["degree",0.0174532925199433]],ID["EPSG",4289]],CONVERSION["RD New",METHOD["Oblique Stereographic",ID["EPSG",9809]],PARAMETER["Latitude of natural origin",52.1561605555556,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8801]],PARAMETER["Longitude of natural origin",5.38763888888889,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8802]],PARAMETER["Scale factor at natural origin",0.9999079,SCALEUNIT["unity",1],ID["EPSG",8805]],PARAMETER["False easting",155000,LENGTHUNIT["metre",1],ID["EPSG",8806]],PARAMETER["False northing",463000,LENGTHUNIT["metre",1],ID["EPSG",8807]]],CS[Cartesian,2],AXIS["easting (X)",east,ORDER[1],LENGTHUNIT["metre",1]],AXIS["northing (Y)",north,ORDER[2],LENGTHUNIT["metre",1]],USAGE[SCOPE["unknown"],AREA["Netherlands - onshore"],BBOX[50.75,3.2,53.7,7.22]],ID["EPSG",28992]] @@ -515,7 +578,7 @@ def my_form_open(dialog, layer, feature): - + PROJCRS["ETRS89-extended / LAEA Europe",BASEGEOGCRS["ETRS89",DATUM["European Terrestrial Reference System 1989",ELLIPSOID["GRS 1980",6378137,298.257222101,LENGTHUNIT["metre",1]]],PRIMEM["Greenwich",0,ANGLEUNIT["degree",0.0174532925199433]],ID["EPSG",4258]],CONVERSION["Europe Equal Area 2001",METHOD["Lambert Azimuthal Equal Area",ID["EPSG",9820]],PARAMETER["Latitude of natural origin",52,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8801]],PARAMETER["Longitude of natural origin",10,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8802]],PARAMETER["False easting",4321000,LENGTHUNIT["metre",1],ID["EPSG",8806]],PARAMETER["False northing",3210000,LENGTHUNIT["metre",1],ID["EPSG",8807]]],CS[Cartesian,2],AXIS["northing (Y)",north,ORDER[1],LENGTHUNIT["metre",1]],AXIS["easting (X)",east,ORDER[2],LENGTHUNIT["metre",1]],USAGE[SCOPE["unknown"],AREA["Europe - LCC & LAEA"],BBOX[24.6,-35.58,84.17,44.83]],ID["EPSG",3035]] @@ -573,7 +636,7 @@ def my_form_open(dialog, layer, feature): custodian - + History1 Alessandro Pasotti @@ -585,18 +648,18 @@ def my_form_open(dialog, layer, feature): - + diff --git a/tests/testdata/qgis_server/landingpage/projects/Project2.qgz b/tests/testdata/qgis_server/landingpage/projects/Project2.qgz index 2dd20d97087b611f69979042dd113c21576ed292..1470ffe5df11f89c5a7705e9236fd4346d635b5d 100644 GIT binary patch literal 8595 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 LpN>fJ-|hbbIh0Hj literal 8183 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 VnXqvG`a=EFF8@rsfBp~<{|3u&%N_s# diff --git a/tests/testdata/qgis_server/landingpage/projects/project_data.gpkg b/tests/testdata/qgis_server/landingpage/projects/project_data.gpkg index 13da706c254e353bb6cd95cbf2cc1721c7712532..4a907892519662bac90413b047c270b08796f649 100644 GIT binary patch delta 51 zcmZozz}~QcT_!ltC$l6~AuYcsH?c&)m_dMnk&(ecL4kpRd*VbHXV!@fdL{~uDO*z* H=bQ%sbsi2m delta 51 zcmZozz}~QcT_!ltC$l6~AuYcsH?c&)m_dMniHX5ML4kpRyKSP3Giw`zp3H{El&vX@ HbIt<*ce)Op diff --git a/tests/testdata/qgis_server/landingpage/projects/test_project_wms_grouped_layers.gpkg b/tests/testdata/qgis_server/landingpage/projects/test_project_wms_grouped_layers.gpkg index 0dfc116091dd59e5d9862539791a39fb8c2b365b..277a67d75a4c6419e2dbe055d89053015e3f5ffc 100644 GIT binary patch delta 579 zcmXw#O=uHA6vtd2!= zc$^UO0(pj5NTob_)*B~dlV3GF0YAW%Jmi1)Z{Frd{0DFFLw-;Xmbek zFG->tc(TdhJ!6E>x1#MspIEV>Uw$#+^hCmF5tl9ye#iahPKTa4v(~P;qkqtj*rB>d z7tBE@lPL_8GZ&y{UA_fu%aF|1!OYAotkw2N#> zy{shVU=|XJY*nC7lZ!=YQJfEb6`6@bfr{`tcyhS}Wqkk!h)K7JxuK!~-Y$PvY&^!+RQD2I2l06mY_TE05}67_C8kA{?zrB`b_L$x%U-~Zu5SKT zPgkJsteEsc7axn@$o&$;dw%YJzj_+|PRyYncJ=(GL^TD&UB4|!tI%01c4DUN#4Nh} E53Z}KrvLx| delta 451 zcmW;EK}Zx)7{KxO=Dm6En>RZ%ZGjNmvaYa2CPE3@rGu{NCPB?%L8bB%T@q2~(!p@2 zF*ilfDdLGiOs8tQUF4UC(6)2qACK zOQg_~q;CM1{ioj9I6<(7FIh2}kWL43c@~F9x8qjOXay8mh#`(_Bup!iJwHz$BUNHO zpx=X=zLkmHJI-5pESuuFy~;1KCd={zJeW$N{QD{_aE{ed5*5t>hmV}c7YC(2tilOB z??RW^bKq>bgeM*yC&4{`->-NtGMCl1^GdeGC;JEg#&+;)mkeRh3=cw6Vq@y4k?QLT z#&tRhXY{QVDC#d);l6o229xCiCY7iV@l{lUyZ&cyJ|opHX9W7>1i!&T(_M#>wYC0_ zndAy|Q8UqiHdXPt8JQc-!xGimRVbRdWk^IGTP~{+|F8GkW9}c-c6Q*T=-7+ApVhXa zGBdTOu+820d;~H#Qh+5#uRVhv9d5v=?pAPIk8Q#Qv%3L#i$9c+Fo&D)f$C7gH4`oZ SFhf)5EHpX=$8-vc&ix0Ry@B`u diff --git a/tests/testdata/qgis_server/landingpage/projects/test_project_wms_grouped_nested_layers.qgs b/tests/testdata/qgis_server/landingpage/projects/test_project_wms_grouped_nested_layers.qgs index 924281cc088f..9f86540e31a7 100644 --- a/tests/testdata/qgis_server/landingpage/projects/test_project_wms_grouped_nested_layers.qgs +++ b/tests/testdata/qgis_server/landingpage/projects/test_project_wms_grouped_nested_layers.qgs @@ -1,5 +1,5 @@ - + QGIS Server - Grouped Nested Layer @@ -22,47 +22,47 @@ - + - + - + - + - + - + - + - + - + @@ -77,16 +77,16 @@ as_areas_no_query_copy2_72792611_056d_4096_9a3b_fa61043136f6 - + - - - - - - - - + + + + + + + + @@ -115,69 +115,117 @@ 0 - - - - - - - - - + + + Annotations_e424930a_3838_4a42_94ca_e28ac525201f + + + + + + + + + + 0 + 0 + + + + + false + + + + + + + + + + + + + + + + + 0 + 0 + + + + + false + + + + + + 1 + - + 614441 5808610 @@ -233,11 +281,11 @@ - false + true - + @@ -261,16 +309,16 @@ 1 1 - + - + - - + + @@ -296,24 +344,28 @@ - + 0 0 1 - - - + + + - - + + + + + + @@ -324,6 +376,7 @@ + @@ -338,7 +391,7 @@ - + - + 614441 5808610 @@ -775,11 +825,11 @@ def my_form_open(dialog, layer, feature): - true + false - + @@ -803,13 +853,13 @@ def my_form_open(dialog, layer, feature): 1 1 - + - + @@ -818,8 +868,8 @@ def my_form_open(dialog, layer, feature): - - + + @@ -840,8 +890,8 @@ def my_form_open(dialog, layer, feature): - - + + @@ -862,8 +912,8 @@ def my_form_open(dialog, layer, feature): - - + + @@ -884,8 +934,8 @@ def my_form_open(dialog, layer, feature): - - + + @@ -906,8 +956,8 @@ def my_form_open(dialog, layer, feature): - - + + @@ -930,8 +980,8 @@ def my_form_open(dialog, layer, feature): - - + + @@ -957,25 +1007,29 @@ def my_form_open(dialog, layer, feature): - - + + 0 0 1 - - - + + + - - + + + + + + @@ -986,6 +1040,7 @@ def my_form_open(dialog, layer, feature): + @@ -1000,7 +1055,7 @@ def my_form_open(dialog, layer, feature): - + - + 614441 5808610 @@ -1460,11 +1495,11 @@ def my_form_open(dialog, layer, feature): - true + false - + @@ -1488,16 +1523,16 @@ def my_form_open(dialog, layer, feature): 1 0 - + - + - - + + @@ -1523,24 +1558,28 @@ def my_form_open(dialog, layer, feature): - + 0 0 1 - - - + + + - - + + + + + + @@ -1551,6 +1590,7 @@ def my_form_open(dialog, layer, feature): + @@ -1565,7 +1605,7 @@ def my_form_open(dialog, layer, feature): - + - + 614441 5808610 @@ -2002,11 +2039,11 @@ def my_form_open(dialog, layer, feature): - true + false - + @@ -2030,16 +2067,16 @@ def my_form_open(dialog, layer, feature): 1 1 - + - + - - + + @@ -2065,24 +2102,28 @@ def my_form_open(dialog, layer, feature): - + 0 0 1 - - - + + + - - + + + + + + @@ -2093,6 +2134,7 @@ def my_form_open(dialog, layer, feature): + @@ -2107,7 +2149,7 @@ def my_form_open(dialog, layer, feature): - + - + 614441 5808610 @@ -2544,11 +2583,11 @@ def my_form_open(dialog, layer, feature): - true + false - + @@ -2572,16 +2611,16 @@ def my_form_open(dialog, layer, feature): 1 1 - + - + - - + + @@ -2607,24 +2646,28 @@ def my_form_open(dialog, layer, feature): - + 0 0 1 - - - + + + - - + + + + + + @@ -2635,6 +2678,7 @@ def my_form_open(dialog, layer, feature): + @@ -2649,7 +2693,7 @@ def my_form_open(dialog, layer, feature): - + - + 613845 5810420 @@ -3076,7 +3117,7 @@ def my_form_open(dialog, layer, feature): - true + false @@ -3096,16 +3137,16 @@ def my_form_open(dialog, layer, feature): 1 1 - + - + - - + + @@ -3146,100 +3187,99 @@ def my_form_open(dialog, layer, feature): - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + 613795 5800450 @@ -3394,11 +3432,11 @@ def my_form_open(dialog, layer, feature): - true + false - + @@ -3422,16 +3460,16 @@ def my_form_open(dialog, layer, feature): 1 1 - + - + - - + + @@ -3465,12 +3503,12 @@ def my_form_open(dialog, layer, feature): - - - - - - + + + + + + @@ -3499,7 +3537,7 @@ def my_form_open(dialog, layer, feature): - + - - - + + + - + 0 0 1 - - - + + + - - + + + + + + @@ -3573,6 +3615,7 @@ def my_form_open(dialog, layer, feature): + @@ -3587,7 +3630,7 @@ def my_form_open(dialog, layer, feature): - + - + 611951 5797780 @@ -4067,11 +4107,11 @@ def my_form_open(dialog, layer, feature): - true + false - + @@ -4095,20 +4135,20 @@ def my_form_open(dialog, layer, feature): 1 1 - + - + - - + + @@ -4129,8 +4169,8 @@ def my_form_open(dialog, layer, feature): - - + + @@ -4153,8 +4193,8 @@ def my_form_open(dialog, layer, feature): - - + + @@ -4180,25 +4220,29 @@ def my_form_open(dialog, layer, feature): - - + + 0 0 1 - - - + + + - - + + + + + + @@ -4209,6 +4253,7 @@ def my_form_open(dialog, layer, feature): + @@ -4223,7 +4268,7 @@ def my_form_open(dialog, layer, feature): - + - + 611425.600499999942258 5808334.13232080265879631 @@ -4432,7 +4474,7 @@ def my_form_open(dialog, layer, feature): - true + false @@ -4452,20 +4494,20 @@ def my_form_open(dialog, layer, feature): 1 0 - + - + - + - + MinMax @@ -4491,8 +4533,8 @@ def my_form_open(dialog, layer, feature): NoEnhancement - - + + resamplingFilter @@ -4689,7 +4731,7 @@ def my_form_open(dialog, layer, feature): - +