1 /***************************************************************************
2     qgsnewarcgisrestconnection.cpp
3                              -------------------
4     begin                : December 2020
5     copyright            : (C) 2020 by Nyall Dawson
6     email                : nyall dot dawson at gmail dot com
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 "qgsnewarcgisrestconnection.h"
18 #include "qgsauthsettingswidget.h"
19 #include "qgssettings.h"
20 #include "qgshelp.h"
21 #include "qgsgui.h"
22 #include "fromencodedcomponenthelper.h"
23 
24 #include <QMessageBox>
25 #include <QUrl>
26 #include <QPushButton>
27 #include <QRegularExpression>
28 #include <QRegularExpressionValidator>
29 #include <QUrlQuery>
30 
QgsNewArcGisRestConnectionDialog(QWidget * parent,const QString & baseKey,const QString & connectionName,Qt::WindowFlags fl)31 QgsNewArcGisRestConnectionDialog::QgsNewArcGisRestConnectionDialog( QWidget *parent, const QString &baseKey, const QString &connectionName, Qt::WindowFlags fl )
32   : QDialog( parent, fl )
33   , mBaseKey( baseKey )
34   , mOriginalConnName( connectionName )
35 {
36   setupUi( this );
37 
38   QgsGui::enableAutoGeometryRestore( this );
39 
40   connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsNewArcGisRestConnectionDialog::showHelp );
41 
42   const QRegularExpression rx( QStringLiteral( "/connections-([^/]+)/" ) );
43   const QRegularExpressionMatch match = rx.match( baseKey );
44   if ( match.hasMatch() )
45   {
46     const QString connectionType( match.captured( 1 ).toUpper() );
47     setWindowTitle( tr( "Create a New %1 Connection" ).arg( connectionType ) );
48   }
49 
50   mCredentialsBaseKey = mBaseKey.split( '-' ).last().toUpper();
51 
52   txtName->setValidator( new QRegularExpressionValidator( QRegularExpression( QStringLiteral( "[^\\/]+" ) ), txtName ) );
53 
54   if ( !connectionName.isEmpty() )
55   {
56     // populate the dialog with the information stored for the connection
57     // populate the fields with the stored setting parameters
58 
59     const QgsSettings settings;
60 
61     const QString key = mBaseKey + connectionName;
62     const QString credentialsKey = "qgis/" + mCredentialsBaseKey + '/' + connectionName;
63     txtName->setText( connectionName );
64     txtUrl->setText( settings.value( key + "/url" ).toString() );
65     mRefererLineEdit->setText( settings.value( key + "/referer" ).toString() );
66 
67     // portal
68     mContentEndPointLineEdit->setText( settings.value( key + "/content_endpoint" ).toString() );
69     mCommunityEndPointLineEdit->setText( settings.value( key + "/community_endpoint" ).toString() );
70 
71     // Authentication
72     mAuthSettings->setUsername( settings.value( credentialsKey + "/username" ).toString() );
73     mAuthSettings->setPassword( settings.value( credentialsKey + "/password" ).toString() );
74     mAuthSettings->setConfigId( settings.value( credentialsKey + "/authcfg" ).toString() );
75   }
76 
77   // Adjust height
78   const int w = width();
79   adjustSize();
80   resize( w, height() );
81 
82   connect( txtName, &QLineEdit::textChanged, this, &QgsNewArcGisRestConnectionDialog::nameChanged );
83   connect( txtUrl, &QLineEdit::textChanged, this, &QgsNewArcGisRestConnectionDialog::urlChanged );
84 
85   buttonBox->button( QDialogButtonBox::Ok )->setDisabled( true );
86   connect( txtName, &QLineEdit::textChanged, this, &QgsNewArcGisRestConnectionDialog::updateOkButtonState );
87   connect( txtUrl, &QLineEdit::textChanged, this, &QgsNewArcGisRestConnectionDialog::updateOkButtonState );
88 
89   nameChanged( connectionName );
90 }
91 
name() const92 QString QgsNewArcGisRestConnectionDialog::name() const
93 {
94   return txtName->text();
95 }
96 
url() const97 QString QgsNewArcGisRestConnectionDialog::url() const
98 {
99   return txtUrl->text();
100 }
101 
nameChanged(const QString & text)102 void QgsNewArcGisRestConnectionDialog::nameChanged( const QString &text )
103 {
104   Q_UNUSED( text )
105   buttonBox->button( QDialogButtonBox::Ok )->setDisabled( txtName->text().isEmpty() || txtUrl->text().isEmpty() );
106 }
107 
urlChanged(const QString & text)108 void QgsNewArcGisRestConnectionDialog::urlChanged( const QString &text )
109 {
110   Q_UNUSED( text )
111   buttonBox->button( QDialogButtonBox::Ok )->setDisabled( txtName->text().isEmpty() || txtUrl->text().isEmpty() );
112 }
113 
updateOkButtonState()114 void QgsNewArcGisRestConnectionDialog::updateOkButtonState()
115 {
116   const bool enabled = !txtName->text().isEmpty() && !txtUrl->text().isEmpty();
117   buttonBox->button( QDialogButtonBox::Ok )->setEnabled( enabled );
118 }
119 
validate()120 bool QgsNewArcGisRestConnectionDialog::validate()
121 {
122   const QgsSettings settings;
123   const QString key = mBaseKey + txtName->text();
124 
125   // warn if entry was renamed to an existing connection
126   if ( ( mOriginalConnName.isNull() || mOriginalConnName.compare( txtName->text(), Qt::CaseInsensitive ) != 0 ) &&
127        settings.contains( key + "/url" ) &&
128        QMessageBox::question( this,
129                               tr( "Save Connection" ),
130                               tr( "Should the existing connection %1 be overwritten?" ).arg( txtName->text() ),
131                               QMessageBox::Ok | QMessageBox::Cancel ) == QMessageBox::Cancel )
132   {
133     return false;
134   }
135 
136   if ( ! mAuthSettings->password().isEmpty() &&
137        QMessageBox::question( this,
138                               tr( "Saving Passwords" ),
139                               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." ),
140                               QMessageBox::Ok | QMessageBox::Cancel ) == QMessageBox::Cancel )
141   {
142     return false;
143   }
144 
145   return true;
146 }
147 
urlTrimmed() const148 QUrl QgsNewArcGisRestConnectionDialog::urlTrimmed() const
149 {
150   QUrl url( txtUrl->text().trimmed() );
151   const QUrlQuery query( url );
152   const QList<QPair<QString, QString> > items = query.queryItems( QUrl::FullyEncoded );
153   QHash< QString, QPair<QString, QString> > params;
154   for ( const QPair<QString, QString> &it : items )
155   {
156     params.insert( it.first.toUpper(), it );
157   }
158 
159   url.setQuery( query );
160 
161   if ( url.path( QUrl::FullyEncoded ).isEmpty() )
162   {
163     url.setPath( fromEncodedComponent_helper( "/" ) );
164   }
165   return url;
166 }
167 
accept()168 void QgsNewArcGisRestConnectionDialog::accept()
169 {
170   QgsSettings settings;
171   const QString key = mBaseKey + txtName->text();
172   const QString credentialsKey = "qgis/" + mCredentialsBaseKey + '/' + txtName->text();
173 
174   if ( !validate() )
175     return;
176 
177   // on rename delete original entry first
178   if ( !mOriginalConnName.isNull() && mOriginalConnName != key )
179   {
180     settings.remove( mBaseKey + mOriginalConnName );
181     settings.remove( "qgis/" + mCredentialsBaseKey + '/' + mOriginalConnName );
182     settings.sync();
183   }
184 
185   const QUrl url( urlTrimmed() );
186   settings.setValue( key + "/url", url.toString() );
187 
188   settings.setValue( credentialsKey + "/username", mAuthSettings->username() );
189   settings.setValue( credentialsKey + "/password", mAuthSettings->password() );
190 
191   settings.setValue( key + "/content_endpoint", mContentEndPointLineEdit->text() );
192   settings.setValue( key + "/community_endpoint", mCommunityEndPointLineEdit->text() );
193 
194   settings.setValue( credentialsKey + "/authcfg", mAuthSettings->configId() );
195 
196   if ( mHttpGroupBox->isVisible() )
197     settings.setValue( key + "/referer", mRefererLineEdit->text() );
198 
199   settings.setValue( mBaseKey + "/selected", txtName->text() );
200 
201   QDialog::accept();
202 }
203 
showHelp()204 void QgsNewArcGisRestConnectionDialog::showHelp()
205 {
206   QgsHelp::openHelp( QStringLiteral( "working_with_ogc/index.html" ) );
207 }
208