diff --git a/python/core/auto_generated/qgsfield.sip.in b/python/core/auto_generated/qgsfield.sip.in index 16acde01af6d..a422d847b677 100644 --- a/python/core/auto_generated/qgsfield.sip.in +++ b/python/core/auto_generated/qgsfield.sip.in @@ -292,6 +292,7 @@ Formats string for display %End + bool convertCompatible( QVariant &v ) const; %Docstring Converts the provided variant to a compatible format diff --git a/python/core/auto_generated/qgsvectorlayer.sip.in b/python/core/auto_generated/qgsvectorlayer.sip.in index b40402cad882..cb0eee31d26c 100644 --- a/python/core/auto_generated/qgsvectorlayer.sip.in +++ b/python/core/auto_generated/qgsvectorlayer.sip.in @@ -1968,24 +1968,36 @@ 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() const /Deprecated/; %Docstring 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 ); + 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 + use setFieldConfigurationFlag instead %End - QSet excludeAttributesWfs() const; + QSet excludeAttributesWfs() const /Deprecated/; %Docstring 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 ); + 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 + use setFieldConfigurationFlag instead %End virtual bool deleteAttribute( int attr ); @@ -2294,6 +2306,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/python/gui/auto_generated/qgscheckablecombobox.sip.in b/python/gui/auto_generated/qgscheckablecombobox.sip.in index fbdb1a28caf4..83c99a6671aa 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`` (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. + +.. versionadded:: 3.16 %End QStringList checkedItems() const; @@ -113,6 +122,7 @@ Toggles the item check state .. seealso:: :py:func:`setItemCheckState` %End + virtual void hidePopup(); %Docstring diff --git a/src/core/qgsfield.cpp b/src/core/qgsfield.cpp index 51ab7ef900c7..9cc7a4e9cbf0 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::ExposeViaWms: + return QStringLiteral( "Expose via WMS" ); + case ConfigurationFlag::ExposeViaWfs: + return QStringLiteral( "Expose via 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 a0e878993c95..a53038e468b2 100644 --- a/src/core/qgsfield.h +++ b/src/core/qgsfield.h @@ -80,8 +80,10 @@ 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) - DefaultFlags = Searchable, //!< Default set of flags for a field + 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 | ExposeViaWms | ExposeViaWfs, //!< Default set of flags for a field }; Q_ENUM( ConfigurationFlag ) Q_DECLARE_FLAGS( ConfigurationFlags, ConfigurationFlag ) @@ -327,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( QgsField::ConfigurationFlag flag ) SIP_SKIP; + #ifndef SIP_RUN /** diff --git a/src/core/qgsvectorlayer.cpp b/src/core/qgsvectorlayer.cpp index 30941209de63..2c26ddcb91bf 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,29 +2304,6 @@ bool QgsVectorLayer::readSymbology( const QDomNode &layerNode, QString &errorMes updateFields(); - //Attributes excluded from WMS and WFS - mExcludeAttributesWMS.clear(); - QDomNode excludeWMSNode = layerNode.namedItem( QStringLiteral( "excludeAttributesWMS" ) ); - if ( !excludeWMSNode.isNull() ) - { - 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() ) - { - QDomNodeList attributeNodeList = excludeWFSNode.toElement().elementsByTagName( QStringLiteral( "attribute" ) ); - for ( int i = 0; i < attributeNodeList.size(); ++i ) - { - mExcludeAttributesWFS.insert( attributeNodeList.at( i ).toElement().text() ); - } - } - // Load editor widget configuration QDomElement widgetsElem = layerNode.namedItem( QStringLiteral( "fieldConfiguration" ) ).toElement(); QDomNodeList fieldConfigurationElementList = widgetsElem.elementsByTagName( QStringLiteral( "field" ) ); @@ -2351,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::ExposeViaWms ), + qMakePair( QStringLiteral( "excludeAttributesWFS" ), QgsField::ConfigurationFlag::ExposeViaWfs ) + }; + 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 ) ) @@ -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 ) ) @@ -5487,12 +5460,19 @@ 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(); } +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..7ed91f01eef2 100644 --- a/src/core/qgsvectorlayer.h +++ b/src/core/qgsvectorlayer.h @@ -1839,23 +1839,27 @@ 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 */ - QSet excludeAttributesWms() 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. + * \deprecated since QGIS 3.16, use setFieldConfigurationFlag instead */ - 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. + * \deprecated since QGIS 3.16, use fields().configurationFlags() instead */ - QSet excludeAttributesWfs() 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. + * \deprecated since QGIS 3.16, use setFieldConfigurationFlag instead */ - 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 +2126,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 diff --git a/src/gui/qgscheckablecombobox.cpp b/src/gui/qgscheckablecombobox.cpp index 0eec32c91eeb..8e286765b50c 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 ); @@ -134,6 +135,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() - 1, state ); +} + QStringList QgsCheckableComboBox::checkedItems() const { QStringList items; diff --git a/src/gui/qgscheckablecombobox.h b/src/gui/qgscheckablecombobox.h index f6d6bb817114..9ed7097d5a6a 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 (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 + */ + void addItemWithCheckState( const QString &text, Qt::CheckState state, const QVariant &userData = QVariant() ); + /** * Returns currently checked items. * \see setCheckedItems() @@ -201,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 SIP_SKIP {return mModel;} + /** * Hides the list of items in the combobox if it is currently * visible and resets the internal state. @@ -260,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..d7a3c20961a6 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" @@ -64,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 ); @@ -163,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: @@ -272,20 +270,19 @@ 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 QList flagList = qgsEnumMap().keys(); + for ( const QgsField::ConfigurationFlag flag : flagList ) + { + if ( flag == QgsField::ConfigurationFlag::None || flag == QgsField::ConfigurationFlag::DefaultFlags ) + continue; + cb->addItemWithCheckState( QgsField::readableConfigurationFlag( flag ), + mLayer->fieldConfigurationFlags( idx ).testFlag( flag ) ? Qt::Checked : Qt::Unchecked, + QVariant::fromValue( flag ) ); + } + mFieldsList->setCellWidget( row, AttrConfigurationFlagsCol, cb ); } bool QgsSourceFieldsProperties::addAttribute( const QgsField &field ) @@ -307,34 +304,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, AttrConfigurationFlagsCol ) ); + 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 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, }; diff --git a/src/server/services/landingpage/qgslandingpageutils.cpp b/src/server/services/landingpage/qgslandingpageutils.cpp index a58b39cb541c..08333c37cc3a 100644 --- a/src/server/services/landingpage/qgslandingpageutils.cpp +++ b/src/server/services/landingpage/qgslandingpageutils.cpp @@ -453,34 +453,38 @@ 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 ) { + // TODO: replace with non deprecated method and fix the tests + // if ( !field.configurationFlags().testFlag( QgsField::ConfigurationFlag::ExposeViaWfs ) ) + Q_NOWARN_DEPRECATED_PUSH if ( vl->excludeAttributesWfs().contains( vl->name() ) ) { ++fieldIdx; continue; } - const auto &constraints { f.constraints().constraints() }; + Q_NOWARN_DEPRECATED_POP + 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..1870f509260f 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::ExposeViaWfs ) ) { continue; } diff --git a/src/server/services/wfs/qgswfsgetfeature.cpp b/src/server/services/wfs/qgswfsgetfeature.cpp index 54a3adbbc02d..6aedb730a5cc 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( "*" ) ) { @@ -259,10 +259,10 @@ namespace QgsWfs // build corresponding propertyname QList propertynames; QList fieldnames; - for ( int idx = 0; idx < fields.count(); ++idx ) + for ( const QgsField &field : fields ) { - fieldnames.append( fields[idx].name() ); - propertynames.append( fields.field( idx ).name().replace( ' ', '_' ).replace( cleanTagNameRegExp, QString() ) ); + fieldnames.append( field.name() ); + propertynames.append( field.name().replace( ' ', '_' ).replace( cleanTagNameRegExp, QString() ) ); } QString fieldName; for ( plstIt = propertyList.constBegin(); plstIt != propertyList.constEnd(); ++plstIt ) @@ -289,20 +289,21 @@ namespace QgsWfs } //excluded attributes for this layer - const QSet &layerExcludedAttributes = vlayer->excludeAttributesWfs(); - if ( !attrIndexes.isEmpty() && !layerExcludedAttributes.isEmpty() ) + if ( !attrIndexes.isEmpty() ) { - foreach ( const QString &excludedAttribute, layerExcludedAttributes ) + for ( const QgsField &field : fields ) { - int fieldNameIdx = fields.indexOf( excludedAttribute ); - if ( fieldNameIdx > -1 && attrIndexes.contains( fieldNameIdx ) ) + if ( !field.configurationFlags().testFlag( QgsField::ConfigurationFlag::ExposeViaWfs ) ) { - attrIndexes.removeOne( fieldNameIdx ); + int fieldNameIdx = fields.indexOf( field.name() ); + 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 0ab43672b0bf..628102d5fa66 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::ExposeViaWfs ) ) { - 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..938c891b25c3 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::ExposeViaWms ) ) { continue; } diff --git a/src/server/services/wms/qgswmsrenderer.cpp b/src/server/services/wms/qgswmsrenderer.cpp index eb593018be57..2a5b7418894d 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::ExposeViaWms ) ) { 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::ExposeViaWms ) ) { continue; } 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 2dd20d97087b..1470ffe5df11 100644 Binary files a/tests/testdata/qgis_server/landingpage/projects/Project2.qgz and b/tests/testdata/qgis_server/landingpage/projects/Project2.qgz differ diff --git a/tests/testdata/qgis_server/landingpage/projects/project_data.gpkg b/tests/testdata/qgis_server/landingpage/projects/project_data.gpkg index 13da706c254e..4a9078925196 100644 Binary files a/tests/testdata/qgis_server/landingpage/projects/project_data.gpkg and b/tests/testdata/qgis_server/landingpage/projects/project_data.gpkg differ 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 0dfc116091dd..277a67d75a4c 100644 Binary files a/tests/testdata/qgis_server/landingpage/projects/test_project_wms_grouped_layers.gpkg and b/tests/testdata/qgis_server/landingpage/projects/test_project_wms_grouped_layers.gpkg differ 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): - +