1 /***************************************************************************
2       qgsamsdataitems.cpp
3       -------------------
4     begin                : Nov 26, 2015
5     copyright            : (C) 2015 by Sandro Mani
6     email                : smani@sourcepole.ch
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 "qgsamsdataitems.h"
18 #include "qgsarcgisrestutils.h"
19 #include "qgsowsconnection.h"
20 #include "qgsproviderregistry.h"
21 #include "qgslogger.h"
22 
23 #ifdef HAVE_GUI
24 #include "qgsamssourceselect.h"
25 #endif
26 
27 #include <QImageReader>
28 
QgsAmsRootItem(QgsDataItem * parent,const QString & name,const QString & path)29 QgsAmsRootItem::QgsAmsRootItem( QgsDataItem *parent, const QString &name, const QString &path )
30   : QgsConnectionsRootItem( parent, name, path, QStringLiteral( "AMS" ) )
31 {
32   mCapabilities |= Fast;
33   mIconName = QStringLiteral( "mIconAms.svg" );
34   populate();
35 }
36 
createChildren()37 QVector<QgsDataItem *> QgsAmsRootItem::createChildren()
38 {
39   QVector<QgsDataItem *> connections;
40 
41   const QStringList connectionList = QgsOwsConnection::connectionList( QStringLiteral( "ARCGISMAPSERVER" ) );
42   for ( const QString &connName : connectionList )
43   {
44     QString path = "ams:/" + connName;
45     connections.append( new QgsAmsConnectionItem( this, connName, path, connName ) );
46   }
47   return connections;
48 }
49 
50 #ifdef HAVE_GUI
paramWidget()51 QWidget *QgsAmsRootItem::paramWidget()
52 {
53   QgsAmsSourceSelect *select = new QgsAmsSourceSelect( nullptr, Qt::WindowFlags(), QgsProviderRegistry::WidgetMode::Manager );
54   connect( select, &QgsArcGisServiceSourceSelect::connectionsChanged, this, &QgsAmsRootItem::onConnectionsChanged );
55   return select;
56 }
57 
onConnectionsChanged()58 void QgsAmsRootItem::onConnectionsChanged()
59 {
60   refresh();
61 }
62 #endif
63 
64 ///////////////////////////////////////////////////////////////////////////////
65 
66 
addFolderItems(QVector<QgsDataItem * > & items,const QVariantMap & serviceData,const QString & baseUrl,const QString & authcfg,const QgsStringMap & headers,QgsDataItem * parent)67 void addFolderItems( QVector< QgsDataItem * > &items, const QVariantMap &serviceData, const QString &baseUrl, const QString &authcfg, const QgsStringMap &headers, QgsDataItem *parent )
68 {
69   QgsArcGisRestUtils::visitFolderItems( [parent, &baseUrl, &items, headers, authcfg]( const QString & name, const QString & url )
70   {
71     std::unique_ptr< QgsAmsFolderItem > folderItem = qgis::make_unique< QgsAmsFolderItem >( parent, name, url, baseUrl, authcfg, headers );
72     items.append( folderItem.release() );
73   }, serviceData, baseUrl );
74 }
75 
addServiceItems(QVector<QgsDataItem * > & items,const QVariantMap & serviceData,const QString & baseUrl,const QString & authcfg,const QgsStringMap & headers,QgsDataItem * parent)76 void addServiceItems( QVector< QgsDataItem * > &items, const QVariantMap &serviceData, const QString &baseUrl, const QString &authcfg, const QgsStringMap &headers, QgsDataItem *parent )
77 {
78   QgsArcGisRestUtils::visitServiceItems(
79     [&items, parent, authcfg, headers]( const QString & name, const QString & url )
80   {
81     std::unique_ptr< QgsAmsServiceItem > serviceItem = qgis::make_unique< QgsAmsServiceItem >( parent, name, url, url, authcfg, headers );
82     items.append( serviceItem.release() );
83   }, serviceData, baseUrl, QgsArcGisRestUtils::Raster );
84 }
85 
addLayerItems(QVector<QgsDataItem * > & items,const QVariantMap & serviceData,const QString & parentUrl,const QString & authcfg,const QgsStringMap & headers,QgsDataItem * parent)86 void addLayerItems( QVector< QgsDataItem * > &items, const QVariantMap &serviceData, const QString &parentUrl, const QString &authcfg, const QgsStringMap &headers, QgsDataItem *parent )
87 {
88   QMap< QString, QgsDataItem * > layerItems;
89   QMap< QString, QString > parents;
90 
91   QgsArcGisRestUtils::addLayerItems( [parent, &layerItems, &parents, authcfg, headers]( const QString & parentLayerId, const QString & id, const QString & name, const QString & description, const QString & url, bool, const QString & authid, const QString & format )
92   {
93     Q_UNUSED( description )
94 
95     if ( !parentLayerId.isEmpty() )
96       parents.insert( id, parentLayerId );
97 
98     std::unique_ptr< QgsAmsLayerItem > layerItem = qgis::make_unique< QgsAmsLayerItem >( parent, name, url, id, name, authid, format, authcfg, headers );
99     layerItems.insert( id, layerItem.release() );
100 
101   }, serviceData, parentUrl, QgsArcGisRestUtils::Raster );
102 
103   // create groups
104   for ( auto it = layerItems.constBegin(); it != layerItems.constEnd(); ++it )
105   {
106     const QString id = it.key();
107     QgsDataItem *item = it.value();
108     const QString parentId = parents.value( id );
109 
110     if ( QgsDataItem *layerParent = parentId.isEmpty() ? nullptr : layerItems.value( parentId ) )
111       layerParent->addChildItem( item );
112     else
113       items.append( item );
114   }
115 }
116 
117 
QgsAmsConnectionItem(QgsDataItem * parent,const QString & name,const QString & path,const QString & connectionName)118 QgsAmsConnectionItem::QgsAmsConnectionItem( QgsDataItem *parent, const QString &name, const QString &path, const QString &connectionName )
119   : QgsDataCollectionItem( parent, name, path, QStringLiteral( "AMS" ) )
120   , mConnName( connectionName )
121 {
122   mIconName = QStringLiteral( "mIconConnect.svg" );
123   mCapabilities |= Collapse;
124 }
125 
createChildren()126 QVector<QgsDataItem *> QgsAmsConnectionItem::createChildren()
127 {
128   const QgsOwsConnection connection( QStringLiteral( "ARCGISMAPSERVER" ), mConnName );
129   const QString url = connection.uri().param( QStringLiteral( "url" ) );
130   const QString authcfg = connection.uri().authConfigId();
131   const QString referer = connection.uri().param( QStringLiteral( "referer" ) );
132   QgsStringMap headers;
133   if ( ! referer.isEmpty() )
134     headers[ QStringLiteral( "Referer" )] = referer;
135 
136   QVector<QgsDataItem *> items;
137   QString errorTitle, errorMessage;
138 
139   QVariantMap serviceData = QgsArcGisRestUtils::getServiceInfo( url, authcfg, errorTitle,  errorMessage, headers );
140   if ( serviceData.isEmpty() )
141   {
142     if ( !errorMessage.isEmpty() )
143     {
144       std::unique_ptr< QgsErrorItem > error = qgis::make_unique< QgsErrorItem >( this, tr( "Connection failed: %1" ).arg( errorTitle ), mPath + "/error" );
145       error->setToolTip( errorMessage );
146       items.append( error.release() );
147       QgsDebugMsg( "Connection failed - " + errorMessage );
148     }
149     return items;
150   }
151 
152   addFolderItems( items, serviceData, url, authcfg, headers, this );
153   addServiceItems( items, serviceData, url, authcfg, headers, this );
154   addLayerItems( items, serviceData, url, authcfg, headers, this );
155 
156   return items;
157 }
158 
equal(const QgsDataItem * other)159 bool QgsAmsConnectionItem::equal( const QgsDataItem *other )
160 {
161   const QgsAmsConnectionItem *o = qobject_cast<const QgsAmsConnectionItem *>( other );
162   return ( type() == other->type() && o && mPath == o->mPath && mName == o->mName );
163 }
164 
url() const165 QString QgsAmsConnectionItem::url() const
166 {
167   const QgsOwsConnection connection( QStringLiteral( "ARCGISMAPSERVER" ), mConnName );
168   return connection.uri().param( QStringLiteral( "url" ) );
169 }
170 
171 
QgsAmsFolderItem(QgsDataItem * parent,const QString & name,const QString & path,const QString & baseUrl,const QString & authcfg,const QgsStringMap & headers)172 QgsAmsFolderItem::QgsAmsFolderItem( QgsDataItem *parent, const QString &name, const QString &path, const QString &baseUrl, const QString &authcfg, const QgsStringMap &headers )
173   : QgsDataCollectionItem( parent, name, path, QStringLiteral( "AMS" ) )
174   , mBaseUrl( baseUrl )
175   , mAuthCfg( authcfg )
176   , mHeaders( headers )
177 {
178   mIconName = QStringLiteral( "mIconDbSchema.svg" );
179   mCapabilities |= Collapse;
180   setToolTip( path );
181 }
182 
183 
createChildren()184 QVector<QgsDataItem *> QgsAmsFolderItem::createChildren()
185 {
186   const QString url = mPath;
187 
188   QVector<QgsDataItem *> items;
189   QString errorTitle, errorMessage;
190   const QVariantMap serviceData = QgsArcGisRestUtils::getServiceInfo( url, mAuthCfg, errorTitle, errorMessage, mHeaders );
191   if ( serviceData.isEmpty() )
192   {
193     if ( !errorMessage.isEmpty() )
194     {
195       std::unique_ptr< QgsErrorItem > error = qgis::make_unique< QgsErrorItem >( this, tr( "Connection failed: %1" ).arg( errorTitle ), mPath + "/error" );
196       error->setToolTip( errorMessage );
197       items.append( error.release() );
198       QgsDebugMsg( "Connection failed - " + errorMessage );
199     }
200     return items;
201   }
202 
203   addFolderItems( items, serviceData, mBaseUrl, mAuthCfg, mHeaders, this );
204   addServiceItems( items, serviceData, mBaseUrl, mAuthCfg, mHeaders, this );
205   addLayerItems( items, serviceData, mPath, mAuthCfg, mHeaders, this );
206   return items;
207 }
208 
equal(const QgsDataItem * other)209 bool QgsAmsFolderItem::equal( const QgsDataItem *other )
210 {
211   const QgsAmsFolderItem *o = qobject_cast<const QgsAmsFolderItem *>( other );
212   return ( type() == other->type() && o && mPath == o->mPath && mName == o->mName );
213 }
214 
215 
QgsAmsServiceItem(QgsDataItem * parent,const QString & name,const QString & path,const QString & baseUrl,const QString & authcfg,const QgsStringMap & headers)216 QgsAmsServiceItem::QgsAmsServiceItem( QgsDataItem *parent, const QString &name, const QString &path, const QString &baseUrl, const QString &authcfg, const QgsStringMap &headers )
217   : QgsDataCollectionItem( parent, name, path, QStringLiteral( "AMS" ) )
218   , mBaseUrl( baseUrl )
219   , mAuthCfg( authcfg )
220   , mHeaders( headers )
221 {
222   mIconName = QStringLiteral( "mIconDbSchema.svg" );
223   mCapabilities |= Collapse;
224   setToolTip( path );
225 }
226 
createChildren()227 QVector<QgsDataItem *> QgsAmsServiceItem::createChildren()
228 {
229   const QString url = mPath;
230 
231   QVector<QgsDataItem *> items;
232   QString errorTitle, errorMessage;
233   const QVariantMap serviceData = QgsArcGisRestUtils::getServiceInfo( url, mAuthCfg, errorTitle, errorMessage, mHeaders );
234   if ( serviceData.isEmpty() )
235   {
236     if ( !errorMessage.isEmpty() )
237     {
238       std::unique_ptr< QgsErrorItem > error = qgis::make_unique< QgsErrorItem >( this, tr( "Connection failed: %1" ).arg( errorTitle ), mPath + "/error" );
239       error->setToolTip( errorMessage );
240       items.append( error.release() );
241       QgsDebugMsg( "Connection failed - " + errorMessage );
242     }
243     return items;
244   }
245 
246   addFolderItems( items, serviceData, mBaseUrl, mAuthCfg, mHeaders, this );
247   addServiceItems( items, serviceData, mBaseUrl, mAuthCfg, mHeaders, this );
248   addLayerItems( items, serviceData, mPath, mAuthCfg, mHeaders, this );
249   return items;
250 }
251 
equal(const QgsDataItem * other)252 bool QgsAmsServiceItem::equal( const QgsDataItem *other )
253 {
254   const QgsAmsServiceItem *o = qobject_cast<const QgsAmsServiceItem *>( other );
255   return ( type() == other->type() && o && mPath == o->mPath && mName == o->mName );
256 }
257 
258 
259 ///////////////////////////////////////////////////////////////////////////////
260 
QgsAmsLayerItem(QgsDataItem * parent,const QString &,const QString & url,const QString & id,const QString & title,const QString & authid,const QString & format,const QString & authcfg,const QgsStringMap & headers)261 QgsAmsLayerItem::QgsAmsLayerItem( QgsDataItem *parent, const QString &, const QString &url, const QString &id, const QString &title, const QString &authid, const QString &format, const QString &authcfg, const QgsStringMap &headers )
262   : QgsLayerItem( parent, title, url, QString(), QgsLayerItem::Raster, QStringLiteral( "arcgismapserver" ) )
263 {
264   QString trimmedUrl = id.isEmpty() ? url : url.left( url.length() - 1 - id.length() ); // trim '/0' from end of url -- AMS provider requires this omitted
265   mUri = QStringLiteral( "crs='%1' format='%2' layer='%3' url='%4'" ).arg( authid, format, id, trimmedUrl );
266   if ( !authcfg.isEmpty() )
267     mUri += QStringLiteral( " authcfg='%1'" ).arg( authcfg );
268   if ( !headers.value( QStringLiteral( "Referer" ) ).isEmpty() )
269     mUri += QStringLiteral( " referer='%1'" ).arg( headers.value( QStringLiteral( "Referer" ) ) );
270   setState( Populated );
271   mIconName = QStringLiteral( "mIconAms.svg" );
272   setToolTip( mPath );
273 }
274 
275 //
276 // QgsAmsDataItemProvider
277 //
278 
name()279 QString QgsAmsDataItemProvider::name()
280 {
281   return QStringLiteral( "AMS" );
282 }
283 
capabilities() const284 int QgsAmsDataItemProvider::capabilities() const
285 {
286   return QgsDataProvider::Net;
287 }
288 
createDataItem(const QString & path,QgsDataItem * parentItem)289 QgsDataItem *QgsAmsDataItemProvider::createDataItem( const QString &path, QgsDataItem *parentItem )
290 {
291   if ( path.isEmpty() )
292   {
293     return new QgsAmsRootItem( parentItem, QObject::tr( "ArcGIS Map Service" ), QStringLiteral( "arcgismapserver:" ) );
294   }
295 
296   // path schema: ams:/connection name (used by OWS)
297   if ( path.startsWith( QLatin1String( "ams:/" ) ) )
298   {
299     QString connectionName = path.split( '/' ).last();
300     if ( QgsOwsConnection::connectionList( QStringLiteral( "arcgismapserver" ) ).contains( connectionName ) )
301     {
302       return new QgsAmsConnectionItem( parentItem, QStringLiteral( "ArcGisMapServer" ), path, connectionName );
303     }
304   }
305 
306   return nullptr;
307 }
308