QGIS API Documentation  3.22.4-Białowieża (ce8e65e95e)
qgsnewhttpconnection.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsnewhttpconnection.cpp - selector for a new HTTP server for WMS, etc.
3  -------------------
4  begin : 3 April 2005
5  copyright : (C) 2005 by Brendan Morley
6  email : morb at ozemail dot com dot au
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 #include "qgsnewhttpconnection.h"
18 #include "qgsauthsettingswidget.h"
19 #include "qgssettings.h"
20 #include "qgshelp.h"
21 #include "qgsgui.h"
23 
24 #include <QMessageBox>
25 #include <QUrl>
26 #include <QPushButton>
27 #include <QRegularExpression>
28 #include <QRegularExpressionValidator>
29 #include <QUrlQuery>
30 
31 QgsNewHttpConnection::QgsNewHttpConnection( QWidget *parent, ConnectionTypes types, const QString &baseKey, const QString &connectionName, QgsNewHttpConnection::Flags flags, Qt::WindowFlags fl )
32  : QDialog( parent, fl )
33  , mTypes( types )
34  , mBaseKey( baseKey )
35  , mOriginalConnName( connectionName )
36 {
37  setupUi( this );
38 
39  if ( !( flags & FlagShowHttpSettings ) )
40  mHttpGroupBox->hide();
41 
43 
44  connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsNewHttpConnection::showHelp );
45 
46  const thread_local QRegularExpression rx( "/connections-([^/]+)/" );
47  const QRegularExpressionMatch rxMatch = rx.match( baseKey );
48  if ( rxMatch.hasMatch() )
49  {
50  QString connectionType( rxMatch.captured( 1 ).toUpper() );
51  if ( connectionType == QLatin1String( "WMS" ) )
52  {
53  connectionType = QStringLiteral( "WMS/WMTS" );
54  }
55  setWindowTitle( tr( "Create a New %1 Connection" ).arg( connectionType ) );
56  }
57 
58  // It would be obviously much better to use mBaseKey also for credentials,
59  // but for some strange reason a different hardcoded key was used instead.
60  // WFS and WMS credentials were mixed with the same key WMS.
61  // Only WMS and WFS providers are using QgsNewHttpConnection at this moment
62  // using connection-wms and connection-wfs -> parse credential key from it.
63  mCredentialsBaseKey = mBaseKey.split( '-' ).last().toUpper();
64 
65  txtName->setValidator( new QRegularExpressionValidator( QRegularExpression( "[^\\/]+" ), txtName ) );
66 
67  cmbDpiMode->clear();
68  cmbDpiMode->addItem( tr( "all" ) );
69  cmbDpiMode->addItem( tr( "off" ) );
70  cmbDpiMode->addItem( tr( "QGIS" ) );
71  cmbDpiMode->addItem( tr( "UMN" ) );
72  cmbDpiMode->addItem( tr( "GeoServer" ) );
73 
74  cmbVersion->clear();
75  cmbVersion->addItem( tr( "Maximum" ) );
76  cmbVersion->addItem( tr( "1.0" ) );
77  cmbVersion->addItem( tr( "1.1" ) );
78  cmbVersion->addItem( tr( "2.0" ) );
79  cmbVersion->addItem( tr( "OGC API - Features" ) );
80  connect( cmbVersion,
81  static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ),
82  this, &QgsNewHttpConnection::wfsVersionCurrentIndexChanged );
83 
84  connect( cbxWfsFeaturePaging, &QCheckBox::stateChanged,
85  this, &QgsNewHttpConnection::wfsFeaturePagingStateChanged );
86 
87  if ( !connectionName.isEmpty() )
88  {
89  // populate the dialog with the information stored for the connection
90  // populate the fields with the stored setting parameters
91 
92  const QgsSettings settings;
93 
94  const QString key = mBaseKey + connectionName;
95  const QString credentialsKey = "qgis/" + mCredentialsBaseKey + '/' + connectionName;
96  txtName->setText( connectionName );
97  txtUrl->setText( settings.value( key + "/url" ).toString() );
98  mRefererLineEdit->setText( settings.value( key + "/referer" ).toString() );
99 
101 
102  // Authentication
103  mAuthSettings->setUsername( settings.value( credentialsKey + "/username" ).toString() );
104  mAuthSettings->setPassword( settings.value( credentialsKey + "/password" ).toString() );
105  mAuthSettings->setConfigId( settings.value( credentialsKey + "/authcfg" ).toString() );
106  }
107  mWfsVersionDetectButton->setDisabled( txtUrl->text().isEmpty() );
108 
109  if ( !( mTypes & ConnectionWms ) && !( mTypes & ConnectionWcs ) )
110  {
111  mWmsOptionsGroupBox->setVisible( false );
112  mGroupBox->layout()->removeWidget( mWmsOptionsGroupBox );
113  }
114  if ( !( mTypes & ConnectionWfs ) )
115  {
116  mWfsOptionsGroupBox->setVisible( false );
117  mGroupBox->layout()->removeWidget( mWfsOptionsGroupBox );
118  }
119  else
120  {
121  txtUrl->setToolTip( tr( "HTTP address of the WFS service, or landing page of a OGC API service<br>(an ending slash might be needed for some OGC API servers)" ) );
122  }
123 
124  if ( mTypes & ConnectionWcs )
125  {
126  cbxIgnoreGetMapURI->setText( tr( "Ignore GetCoverage URI reported in capabilities" ) );
127  cbxWmsIgnoreAxisOrientation->setText( tr( "Ignore axis orientation" ) );
128  if ( !( mTypes & ConnectionWms ) )
129  {
130  mWmsOptionsGroupBox->setTitle( tr( "WCS Options" ) );
131 
132  cbxIgnoreGetFeatureInfoURI->setVisible( false );
133  mGroupBox->layout()->removeWidget( cbxIgnoreGetFeatureInfoURI );
134 
135  cmbDpiMode->setVisible( false );
136  mGroupBox->layout()->removeWidget( cmbDpiMode );
137  lblDpiMode->setVisible( false );
138  mGroupBox->layout()->removeWidget( lblDpiMode );
139  }
140  }
141 
142  if ( !( flags & FlagShowTestConnection ) )
143  {
144  mTestConnectionButton->hide();
145  mGroupBox->layout()->removeWidget( mTestConnectionButton );
146  }
147 
148  if ( flags & FlagHideAuthenticationGroup )
149  {
150  mAuthGroupBox->hide();
151  mGroupBox->layout()->removeWidget( mAuthGroupBox );
152  }
153  // Adjust height
154  const int w = width();
155  adjustSize();
156  resize( w, height() );
157 
158  connect( txtName, &QLineEdit::textChanged, this, &QgsNewHttpConnection::nameChanged );
159  connect( txtUrl, &QLineEdit::textChanged, this, &QgsNewHttpConnection::urlChanged );
160 
161  buttonBox->button( QDialogButtonBox::Ok )->setDisabled( true );
162  connect( txtName, &QLineEdit::textChanged, this, &QgsNewHttpConnection::updateOkButtonState );
163  connect( txtUrl, &QLineEdit::textChanged, this, &QgsNewHttpConnection::updateOkButtonState );
164 
165  nameChanged( connectionName );
166 }
167 
168 void QgsNewHttpConnection::wfsVersionCurrentIndexChanged( int index )
169 {
170  // For now 2019-06-06, leave paging checkable for some WFS version 1.1 servers with support
171  cbxWfsFeaturePaging->setEnabled( index == WFS_VERSION_MAX || index >= WFS_VERSION_2_0 );
172  lblPageSize->setEnabled( cbxWfsFeaturePaging->isChecked() && ( index == WFS_VERSION_MAX || index >= WFS_VERSION_1_1 ) );
173  txtPageSize->setEnabled( cbxWfsFeaturePaging->isChecked() && ( index == WFS_VERSION_MAX || index >= WFS_VERSION_1_1 ) );
174  cbxWfsIgnoreAxisOrientation->setEnabled( index != WFS_VERSION_1_0 && index != WFS_VERSION_API_FEATURES_1_0 );
175  cbxWfsInvertAxisOrientation->setEnabled( index != WFS_VERSION_API_FEATURES_1_0 );
176  wfsUseGml2EncodingForTransactions()->setEnabled( index == WFS_VERSION_1_1 );
177 }
178 
179 void QgsNewHttpConnection::wfsFeaturePagingStateChanged( int state )
180 {
181  lblPageSize->setEnabled( state == Qt::Checked );
182  txtPageSize->setEnabled( state == Qt::Checked );
183 }
184 
186 {
187  return txtName->text();
188 }
189 
191 {
192  return txtUrl->text();
193 }
194 
195 void QgsNewHttpConnection::nameChanged( const QString &text )
196 {
197  Q_UNUSED( text )
198  buttonBox->button( QDialogButtonBox::Ok )->setDisabled( txtName->text().isEmpty() || txtUrl->text().isEmpty() );
199 }
200 
201 void QgsNewHttpConnection::urlChanged( const QString &text )
202 {
203  Q_UNUSED( text )
204  buttonBox->button( QDialogButtonBox::Ok )->setDisabled( txtName->text().isEmpty() || txtUrl->text().isEmpty() );
205  mWfsVersionDetectButton->setDisabled( txtUrl->text().isEmpty() );
206 }
207 
208 void QgsNewHttpConnection::updateOkButtonState()
209 {
210  const bool enabled = !txtName->text().isEmpty() && !txtUrl->text().isEmpty();
211  buttonBox->button( QDialogButtonBox::Ok )->setEnabled( enabled );
212 }
213 
215 {
216  const QgsSettings settings;
217  const QString key = mBaseKey + txtName->text();
218 
219  // warn if entry was renamed to an existing connection
220  if ( ( mOriginalConnName.isNull() || mOriginalConnName.compare( txtName->text(), Qt::CaseInsensitive ) != 0 ) &&
221  settings.contains( key + "/url" ) &&
222  QMessageBox::question( this,
223  tr( "Save Connection" ),
224  tr( "Should the existing connection %1 be overwritten?" ).arg( txtName->text() ),
225  QMessageBox::Ok | QMessageBox::Cancel ) == QMessageBox::Cancel )
226  {
227  return false;
228  }
229 
230  if ( ! mAuthSettings->password().isEmpty() &&
231  QMessageBox::question( this,
232  tr( "Saving Passwords" ),
233  tr( "WARNING: You have entered a password. It will be stored in unsecured plain text in your project files and your home directory (Unix-like OS) or user profile (Windows). If you want to avoid this, press Cancel and either:\n\na) Don't provide a password in the connection settings — it will be requested interactively when needed;\nb) Use the Configuration tab to add your credentials in an HTTP Basic Authentication method and store them in an encrypted database." ),
234  QMessageBox::Ok | QMessageBox::Cancel ) == QMessageBox::Cancel )
235  {
236  return false;
237  }
238 
239  return true;
240 }
241 
243 {
244  return mTestConnectionButton;
245 }
246 
248 {
249  return mAuthSettings;
250 }
251 
253 {
254  return mWfsVersionDetectButton;
255 }
256 
258 {
259  return cmbVersion;
260 }
261 
263 {
264  return cbxWfsFeaturePaging;
265 }
266 
268 {
269  return cbxWfsUseGml2EncodingForTransactions;
270 }
271 
273 {
274  return txtPageSize;
275 }
276 
277 QString QgsNewHttpConnection::wfsSettingsKey( const QString &base, const QString &connectionName ) const
278 {
279  return base + connectionName;
280 }
281 
282 QString QgsNewHttpConnection::wmsSettingsKey( const QString &base, const QString &connectionName ) const
283 {
284  return base + connectionName;
285 }
286 
288 {
289  const QgsSettings settings;
290  const QString wfsKey = wfsSettingsKey( mBaseKey, mOriginalConnName );
291  const QString wmsKey = wmsSettingsKey( mBaseKey, mOriginalConnName );
292 
293  cbxIgnoreGetMapURI->setChecked( settings.value( wmsKey + "/ignoreGetMapURI", false ).toBool() );
294  cbxWmsIgnoreReportedLayerExtents->setChecked( settings.value( wmsKey + QStringLiteral( "/ignoreReportedLayerExtents" ), false ).toBool() );
295  cbxWfsIgnoreAxisOrientation->setChecked( settings.value( wfsKey + "/ignoreAxisOrientation", false ).toBool() );
296  cbxWfsInvertAxisOrientation->setChecked( settings.value( wfsKey + "/invertAxisOrientation", false ).toBool() );
297  cbxWfsUseGml2EncodingForTransactions->setChecked( settings.value( wfsKey + "/preferCoordinatesForWfsT11", false ).toBool() );
298 
299  cbxWmsIgnoreAxisOrientation->setChecked( settings.value( wmsKey + "/ignoreAxisOrientation", false ).toBool() );
300  cbxWmsInvertAxisOrientation->setChecked( settings.value( wmsKey + "/invertAxisOrientation", false ).toBool() );
301  cbxIgnoreGetFeatureInfoURI->setChecked( settings.value( wmsKey + "/ignoreGetFeatureInfoURI", false ).toBool() );
302  cbxSmoothPixmapTransform->setChecked( settings.value( wmsKey + "/smoothPixmapTransform", false ).toBool() );
303 
304  int dpiIdx;
305  switch ( settings.value( wmsKey + "/dpiMode", 7 ).toInt() )
306  {
307  case 0: // off
308  dpiIdx = 1;
309  break;
310  case 1: // QGIS
311  dpiIdx = 2;
312  break;
313  case 2: // UMN
314  dpiIdx = 3;
315  break;
316  case 4: // GeoServer
317  dpiIdx = 4;
318  break;
319  default: // other => all
320  dpiIdx = 0;
321  break;
322  }
323  cmbDpiMode->setCurrentIndex( dpiIdx );
324 
325  const QString version = settings.value( wfsKey + "/version" ).toString();
326  int versionIdx = WFS_VERSION_MAX; // AUTO
327  if ( version == QLatin1String( "1.0.0" ) )
328  versionIdx = WFS_VERSION_1_0;
329  else if ( version == QLatin1String( "1.1.0" ) )
330  versionIdx = WFS_VERSION_1_1;
331  else if ( version == QLatin1String( "2.0.0" ) )
332  versionIdx = WFS_VERSION_2_0;
333  else if ( version == QLatin1String( "OGC_API_FEATURES" ) )
334  versionIdx = WFS_VERSION_API_FEATURES_1_0;
335  cmbVersion->setCurrentIndex( versionIdx );
336 
337  // Enable/disable these items per WFS versions
338  wfsVersionCurrentIndexChanged( versionIdx );
339 
340  mRefererLineEdit->setText( settings.value( wmsKey + "/referer" ).toString() );
341  txtMaxNumFeatures->setText( settings.value( wfsKey + "/maxnumfeatures" ).toString() );
342 
343  // Only default to paging enabled if WFS 2.0.0 or higher
344  const bool pagingEnabled = settings.value( wfsKey + "/pagingenabled", ( versionIdx == WFS_VERSION_MAX || versionIdx >= WFS_VERSION_2_0 ) ).toBool();
345  txtPageSize->setText( settings.value( wfsKey + "/pagesize" ).toString() );
346  cbxWfsFeaturePaging->setChecked( pagingEnabled );
347 }
348 
350 {
351  QUrl url( txtUrl->text().trimmed() );
352  QUrlQuery query( url );
353  const QList<QPair<QString, QString> > items = query.queryItems( QUrl::FullyEncoded );
354  QHash< QString, QPair<QString, QString> > params;
355  for ( const QPair<QString, QString> &it : items )
356  {
357  params.insert( it.first.toUpper(), it );
358  }
359 
360  if ( params[QStringLiteral( "SERVICE" )].second.toUpper() == "WMS" ||
361  params[QStringLiteral( "SERVICE" )].second.toUpper() == "WFS" ||
362  params[QStringLiteral( "SERVICE" )].second.toUpper() == "WCS" )
363  {
364  query.removeQueryItem( params.value( QStringLiteral( "SERVICE" ) ).first );
365  query.removeQueryItem( params.value( QStringLiteral( "REQUEST" ) ).first );
366  query.removeQueryItem( params.value( QStringLiteral( "FORMAT" ) ).first );
367  }
368 
369  url.setQuery( query );
370 
371  if ( url.path( QUrl::FullyEncoded ).isEmpty() )
372  {
373  url.setPath( fromEncodedComponent_helper( "/" ) );
374  }
375  return url;
376 }
377 
379 {
380  QgsSettings settings;
381  const QString key = mBaseKey + txtName->text();
382  const QString credentialsKey = "qgis/" + mCredentialsBaseKey + '/' + txtName->text();
383 
384  if ( !validate() )
385  return;
386 
387  // on rename delete original entry first
388  if ( !mOriginalConnName.isNull() && mOriginalConnName != key )
389  {
390  settings.remove( mBaseKey + mOriginalConnName );
391  settings.remove( "qgis/" + mCredentialsBaseKey + '/' + mOriginalConnName );
392  settings.sync();
393  }
394 
395  const QUrl url( urlTrimmed() );
396  settings.setValue( key + "/url", url.toString() );
397 
398  const QString wfsKey = wfsSettingsKey( mBaseKey, txtName->text() );
399  const QString wmsKey = wmsSettingsKey( mBaseKey, txtName->text() );
400 
401  if ( mTypes & ConnectionWfs )
402  {
403  settings.setValue( wfsKey + "/ignoreAxisOrientation", cbxWfsIgnoreAxisOrientation->isChecked() );
404  settings.setValue( wfsKey + "/invertAxisOrientation", cbxWfsInvertAxisOrientation->isChecked() );
405  settings.setValue( wfsKey + "/preferCoordinatesForWfsT11", cbxWfsUseGml2EncodingForTransactions->isChecked() );
406  }
407  if ( mTypes & ConnectionWms || mTypes & ConnectionWcs )
408  {
409  settings.setValue( wmsKey + "/ignoreAxisOrientation", cbxWmsIgnoreAxisOrientation->isChecked() );
410  settings.setValue( wmsKey + "/invertAxisOrientation", cbxWmsInvertAxisOrientation->isChecked() );
411 
412  settings.setValue( wmsKey + QStringLiteral( "/ignoreReportedLayerExtents" ), cbxWmsIgnoreReportedLayerExtents->isChecked() );
413  settings.setValue( wmsKey + "/ignoreGetMapURI", cbxIgnoreGetMapURI->isChecked() );
414  settings.setValue( wmsKey + "/smoothPixmapTransform", cbxSmoothPixmapTransform->isChecked() );
415 
416  int dpiMode = 0;
417  switch ( cmbDpiMode->currentIndex() )
418  {
419  case 0: // all => QGIS|UMN|GeoServer
420  dpiMode = 7;
421  break;
422  case 1: // off
423  dpiMode = 0;
424  break;
425  case 2: // QGIS
426  dpiMode = 1;
427  break;
428  case 3: // UMN
429  dpiMode = 2;
430  break;
431  case 4: // GeoServer
432  dpiMode = 4;
433  break;
434  }
435 
436  settings.setValue( wmsKey + "/dpiMode", dpiMode );
437 
438  settings.setValue( wmsKey + "/referer", mRefererLineEdit->text() );
439  }
440  if ( mTypes & ConnectionWms )
441  {
442  settings.setValue( wmsKey + "/ignoreGetFeatureInfoURI", cbxIgnoreGetFeatureInfoURI->isChecked() );
443  }
444  if ( mTypes & ConnectionWfs )
445  {
446  QString version = QStringLiteral( "auto" );
447  switch ( cmbVersion->currentIndex() )
448  {
449  case WFS_VERSION_MAX:
450  version = QStringLiteral( "auto" );
451  break;
452  case WFS_VERSION_1_0:
453  version = QStringLiteral( "1.0.0" );
454  break;
455  case WFS_VERSION_1_1:
456  version = QStringLiteral( "1.1.0" );
457  break;
458  case WFS_VERSION_2_0:
459  version = QStringLiteral( "2.0.0" );
460  break;
462  version = QStringLiteral( "OGC_API_FEATURES" );
463  break;
464  }
465  settings.setValue( wfsKey + "/version", version );
466 
467  settings.setValue( wfsKey + "/maxnumfeatures", txtMaxNumFeatures->text() );
468 
469  settings.setValue( wfsKey + "/pagesize", txtPageSize->text() );
470  settings.setValue( wfsKey + "/pagingenabled", cbxWfsFeaturePaging->isChecked() );
471  }
472 
473  settings.setValue( credentialsKey + "/username", mAuthSettings->username() );
474  settings.setValue( credentialsKey + "/password", mAuthSettings->password() );
475 
476  settings.setValue( credentialsKey + "/authcfg", mAuthSettings->configId() );
477 
478  if ( mHttpGroupBox->isVisible() )
479  settings.setValue( key + "/referer", mRefererLineEdit->text() );
480 
481  settings.setValue( mBaseKey + "/selected", txtName->text() );
482 
483  QDialog::accept();
484 }
485 
486 void QgsNewHttpConnection::showHelp()
487 {
488  QgsHelp::openHelp( QStringLiteral( "working_with_ogc/index.html" ) );
489 }
Widget for entering authentication credentials both in the form username/password and by using QGIS A...
static void enableAutoGeometryRestore(QWidget *widget, const QString &key=QString())
Register the widget to allow its position to be automatically saved and restored when open and closed...
Definition: qgsgui.cpp:168
static void openHelp(const QString &key)
Opens help topic for the given help key using default system web browser.
Definition: qgshelp.cpp:36
QgsNewHttpConnection(QWidget *parent=nullptr, QgsNewHttpConnection::ConnectionTypes types=ConnectionWms, const QString &baseKey="qgis/connections-wms/", const QString &connectionName=QString(), QgsNewHttpConnection::Flags flags=QgsNewHttpConnection::Flags(), Qt::WindowFlags fl=QgsGuiUtils::ModalDialogFlags)
Constructor for QgsNewHttpConnection.
QPushButton * testConnectButton()
Returns the "test connection" button.
QgsAuthSettingsWidget * authSettingsWidget()
Returns the current authentication settings widget.
QCheckBox * wfsPagingEnabledCheckBox()
Returns the "WFS paging enabled" checkbox.
virtual bool validate()
Returns true if dialog settings are valid, or false if current settings are not valid and the dialog ...
QString name() const
Returns the current connection name.
@ FlagShowHttpSettings
Display the 'http' group.
@ FlagHideAuthenticationGroup
Hide the Authentication group.
@ FlagShowTestConnection
Display the 'test connection' button.
QCheckBox * wfsUseGml2EncodingForTransactions()
Returns the "Use GML2 encoding for transactions" checkbox.
QComboBox * wfsVersionComboBox()
Returns the "WFS version" combobox.
virtual QString wmsSettingsKey(const QString &base, const QString &connectionName) const
Returns the QSettings key for WMS related settings for the connection.
virtual QString wfsSettingsKey(const QString &base, const QString &connectionName) const
Returns the QSettings key for WFS related settings for the connection.
QString url() const
Returns the current connection url.
QPushButton * wfsVersionDetectButton()
Returns the "WFS version detect" button.
QUrl urlTrimmed() const
Returns the url.
QLineEdit * wfsPageSizeLineEdit()
Returns the "WFS page size" edit.
void updateServiceSpecificSettings()
Triggers a resync of the GUI widgets for the service specific settings (i.e.
@ ConnectionWms
WMS connection.
@ ConnectionWfs
WFS connection.
@ ConnectionWcs
WCS connection.
This class is a composition of two QSettings instances:
Definition: qgssettings.h:62
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
bool contains(const QString &key, QgsSettings::Section section=QgsSettings::NoSection) const
Returns true if there exists a setting called key; returns false otherwise.
void sync()
Writes any unsaved changes to permanent storage, and reloads any settings that have been changed in t...
void remove(const QString &key, QgsSettings::Section section=QgsSettings::NoSection)
Removes the setting key and any sub-settings of key in a section.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
QString fromEncodedComponent_helper(const QByteArray &ba)