1 /***************************************************************************
2 qgsarcgisrestdataitems.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 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15 #include "qgsarcgisrestdataitems.h"
16 #include "qgslogger.h"
17 #include "qgsowsconnection.h"
18 #include "qgsarcgisrestutils.h"
19 #include "qgsarcgisrestquery.h"
20 #include "qgsarcgisportalutils.h"
21 #include "qgsdataprovider.h"
22
23 #ifdef HAVE_GUI
24 #include "qgsarcgisrestsourceselect.h"
25 #endif
26
27 #include <QMessageBox>
28 #include <QCoreApplication>
29 #include <QSettings>
30 #include <QUrl>
31 #include "qgssettings.h"
32
33
34 //
35 // QgsArcGisRestRootItem
36 //
37
QgsArcGisRestRootItem(QgsDataItem * parent,const QString & name,const QString & path)38 QgsArcGisRestRootItem::QgsArcGisRestRootItem( QgsDataItem *parent, const QString &name, const QString &path )
39 : QgsConnectionsRootItem( parent, name, path, QStringLiteral( "AFS" ) )
40 {
41 mCapabilities |= Qgis::BrowserItemCapability::Fast;
42 mIconName = QStringLiteral( "mIconAfs.svg" );
43 populate();
44 }
45
createChildren()46 QVector<QgsDataItem *> QgsArcGisRestRootItem::createChildren()
47 {
48 QVector<QgsDataItem *> connections;
49
50 const QStringList connectionList = QgsOwsConnection::connectionList( QStringLiteral( "ARCGISFEATURESERVER" ) );
51 for ( const QString &connName : connectionList )
52 {
53 const QString path = QStringLiteral( "afs:/" ) + connName;
54 connections.append( new QgsArcGisRestConnectionItem( this, connName, path, connName ) );
55 }
56
57 return connections;
58 }
59
60 #ifdef HAVE_GUI
paramWidget()61 QWidget *QgsArcGisRestRootItem::paramWidget()
62 {
63 QgsArcGisRestSourceSelect *select = new QgsArcGisRestSourceSelect( nullptr, Qt::WindowFlags(), QgsProviderRegistry::WidgetMode::Manager );
64 connect( select, &QgsArcGisRestSourceSelect::connectionsChanged, this, &QgsArcGisRestRootItem::onConnectionsChanged );
65 return select;
66 }
67
onConnectionsChanged()68 void QgsArcGisRestRootItem::onConnectionsChanged()
69 {
70 refresh();
71 }
72 #endif
73
74 ///////////////////////////////////////////////////////////////////////////////
75
addFolderItems(QVector<QgsDataItem * > & items,const QVariantMap & serviceData,const QString & baseUrl,const QString & authcfg,const QgsStringMap & headers,QgsDataItem * parent,const QString & supportedFormats)76 void addFolderItems( QVector< QgsDataItem * > &items, const QVariantMap &serviceData, const QString &baseUrl, const QString &authcfg, const QgsStringMap &headers, QgsDataItem *parent,
77 const QString &supportedFormats )
78 {
79 QgsArcGisRestQueryUtils::visitFolderItems( [parent, &baseUrl, &items, headers, authcfg, supportedFormats]( const QString & name, const QString & url )
80 {
81 std::unique_ptr< QgsArcGisRestFolderItem > folderItem = std::make_unique< QgsArcGisRestFolderItem >( parent, name, url, baseUrl, authcfg, headers );
82 folderItem->setSupportedFormats( supportedFormats );
83 items.append( folderItem.release() );
84 }, serviceData, baseUrl );
85 }
86
addServiceItems(QVector<QgsDataItem * > & items,const QVariantMap & serviceData,const QString & baseUrl,const QString & authcfg,const QgsStringMap & headers,QgsDataItem * parent,const QString & supportedFormats)87 void addServiceItems( QVector< QgsDataItem * > &items, const QVariantMap &serviceData, const QString &baseUrl, const QString &authcfg, const QgsStringMap &headers, QgsDataItem *parent,
88 const QString &supportedFormats )
89 {
90 QgsArcGisRestQueryUtils::visitServiceItems(
91 [&items, parent, authcfg, headers, supportedFormats]( const QString & name, const QString & url, const QString & service, QgsArcGisRestQueryUtils::ServiceTypeFilter serviceType )
92 {
93 switch ( serviceType )
94 {
95 case QgsArcGisRestQueryUtils::Raster:
96 {
97 std::unique_ptr< QgsArcGisMapServiceItem > serviceItem = std::make_unique< QgsArcGisMapServiceItem >( parent, name, url, url, authcfg, headers, service );
98 items.append( serviceItem.release() );
99 break;
100 }
101
102 case QgsArcGisRestQueryUtils::Vector:
103 {
104 std::unique_ptr< QgsArcGisFeatureServiceItem > serviceItem = std::make_unique< QgsArcGisFeatureServiceItem >( parent, name, url, url, authcfg, headers );
105 serviceItem->setSupportedFormats( supportedFormats );
106 items.append( serviceItem.release() );
107 break;
108 }
109
110 case QgsArcGisRestQueryUtils::AllTypes:
111 break;
112 }
113 }, serviceData, baseUrl );
114 }
115
addLayerItems(QVector<QgsDataItem * > & items,const QVariantMap & serviceData,const QString & parentUrl,const QString & authcfg,const QgsStringMap & headers,QgsDataItem * parent,QgsArcGisRestQueryUtils::ServiceTypeFilter serviceTypeFilter,const QString & supportedFormats)116 void addLayerItems( QVector< QgsDataItem * > &items, const QVariantMap &serviceData, const QString &parentUrl, const QString &authcfg, const QgsStringMap &headers, QgsDataItem *parent, QgsArcGisRestQueryUtils::ServiceTypeFilter serviceTypeFilter,
117 const QString &supportedFormats )
118 {
119 QMultiMap< QString, QgsDataItem * > layerItems;
120 QMap< QString, QString > parents;
121
122 QgsArcGisRestQueryUtils::addLayerItems( [parent, &layerItems, &parents, authcfg, headers, serviceTypeFilter, supportedFormats]( const QString & parentLayerId, QgsArcGisRestQueryUtils::ServiceTypeFilter serviceType, QgsWkbTypes::GeometryType geometryType, const QString & id, const QString & name, const QString & description, const QString & url, bool isParent, const QString & authid, const QString & format )
123 {
124 Q_UNUSED( description )
125
126 if ( !parentLayerId.isEmpty() )
127 parents.insert( id, parentLayerId );
128
129 if ( isParent && serviceType != QgsArcGisRestQueryUtils::Raster )
130 {
131 if ( !layerItems.value( id ) )
132 {
133 std::unique_ptr< QgsArcGisRestParentLayerItem > layerItem = std::make_unique< QgsArcGisRestParentLayerItem >( parent, name, url, authcfg, headers );
134 layerItems.insert( id, layerItem.release() );
135 }
136 }
137 else
138 {
139 std::unique_ptr< QgsDataItem > layerItem;
140 switch ( serviceTypeFilter == QgsArcGisRestQueryUtils::AllTypes ? serviceType : serviceTypeFilter )
141 {
142 case QgsArcGisRestQueryUtils::Vector:
143 layerItem = std::make_unique< QgsArcGisFeatureServiceLayerItem >( parent, name, url, name, authid, authcfg, headers, geometryType == QgsWkbTypes::PolygonGeometry ? Qgis::BrowserLayerType::Polygon :
144 geometryType == QgsWkbTypes::LineGeometry ? Qgis::BrowserLayerType::Line
145 : geometryType == QgsWkbTypes::PointGeometry ? Qgis::BrowserLayerType::Point : Qgis::BrowserLayerType::Vector );
146 break;
147
148 case QgsArcGisRestQueryUtils::Raster:
149 layerItem = std::make_unique< QgsArcGisMapServiceLayerItem >( parent, name, url, id, name, authid, format, authcfg, headers );
150 static_cast< QgsArcGisMapServiceLayerItem * >( layerItem.get() )->setSupportedFormats( supportedFormats );
151 break;
152
153 case QgsArcGisRestQueryUtils::AllTypes:
154 break;
155 }
156 if ( layerItem )
157 layerItems.insert( id, layerItem.release() );
158 }
159
160 }, serviceData, parentUrl, supportedFormats, serviceTypeFilter );
161
162 // create groups
163 for ( auto it = layerItems.constBegin(); it != layerItems.constEnd(); ++it )
164 {
165 const QString id = it.key();
166 QgsDataItem *item = it.value();
167 const QString parentId = parents.value( id );
168
169 if ( QgsDataItem *layerParent = parentId.isEmpty() ? nullptr : layerItems.value( parentId ) )
170 layerParent->addChildItem( item );
171 else
172 items.append( item );
173 }
174 }
175
176 //
177 // QgsArcGisRestConnectionItem
178 //
179
QgsArcGisRestConnectionItem(QgsDataItem * parent,const QString & name,const QString & path,const QString & connectionName)180 QgsArcGisRestConnectionItem::QgsArcGisRestConnectionItem( QgsDataItem *parent, const QString &name, const QString &path, const QString &connectionName )
181 : QgsDataCollectionItem( parent, name, path, QStringLiteral( "AFS" ) )
182 , mConnName( connectionName )
183 {
184 mIconName = QStringLiteral( "mIconConnect.svg" );
185 mCapabilities |= Qgis::BrowserItemCapability::Collapse;
186
187 const QgsSettings settings;
188 const QString key = QStringLiteral( "qgis/connections-arcgisfeatureserver/" ) + mConnName;
189 mPortalContentEndpoint = settings.value( key + "/content_endpoint" ).toString();
190 mPortalCommunityEndpoint = settings.value( key + "/community_endpoint" ).toString();
191 }
192
createChildren()193 QVector<QgsDataItem *> QgsArcGisRestConnectionItem::createChildren()
194 {
195 const QgsOwsConnection connection( QStringLiteral( "ARCGISFEATURESERVER" ), mConnName );
196 const QString url = connection.uri().param( QStringLiteral( "url" ) );
197 const QString authcfg = connection.uri().authConfigId();
198 const QString referer = connection.uri().param( QStringLiteral( "referer" ) );
199 QgsStringMap headers;
200 if ( ! referer.isEmpty() )
201 headers[ QStringLiteral( "Referer" )] = referer;
202
203 QVector<QgsDataItem *> items;
204 if ( !mPortalCommunityEndpoint.isEmpty() && !mPortalContentEndpoint.isEmpty() )
205 {
206 items << new QgsArcGisPortalGroupsItem( this, QStringLiteral( "groups" ), authcfg, headers, mPortalCommunityEndpoint, mPortalContentEndpoint );
207 items << new QgsArcGisRestServicesItem( this, url, QStringLiteral( "services" ), authcfg, headers );
208 }
209 else
210 {
211 QString errorTitle, errorMessage;
212 const QVariantMap serviceData = QgsArcGisRestQueryUtils::getServiceInfo( url, authcfg, errorTitle, errorMessage, headers );
213 if ( serviceData.isEmpty() )
214 {
215 if ( !errorMessage.isEmpty() )
216 {
217 std::unique_ptr< QgsErrorItem > error = std::make_unique< QgsErrorItem >( this, tr( "Connection failed: %1" ).arg( errorTitle ), mPath + "/error" );
218 error->setToolTip( errorMessage );
219 items.append( error.release() );
220 QgsDebugMsg( "Connection failed - " + errorMessage );
221 }
222 return items;
223 }
224
225 addFolderItems( items, serviceData, url, authcfg, headers, this, QString() );
226 addServiceItems( items, serviceData, url, authcfg, headers, this, QString() );
227 addLayerItems( items, serviceData, url, authcfg, headers, this, QgsArcGisRestQueryUtils::AllTypes, QString() );
228 }
229
230 return items;
231 }
232
equal(const QgsDataItem * other)233 bool QgsArcGisRestConnectionItem::equal( const QgsDataItem *other )
234 {
235 const QgsArcGisRestConnectionItem *o = qobject_cast<const QgsArcGisRestConnectionItem *>( other );
236 return ( type() == other->type() && o && mPath == o->mPath && mName == o->mName );
237 }
238
url() const239 QString QgsArcGisRestConnectionItem::url() const
240 {
241 const QgsOwsConnection connection( QStringLiteral( "ARCGISFEATURESERVER" ), mConnName );
242 return connection.uri().param( QStringLiteral( "url" ) );
243 }
244
245
246 //
247 // QgsArcGisPortalGroupsItem
248 //
249
QgsArcGisPortalGroupsItem(QgsDataItem * parent,const QString & path,const QString & authcfg,const QgsStringMap & headers,const QString & communityEndpoint,const QString & contentEndpoint)250 QgsArcGisPortalGroupsItem::QgsArcGisPortalGroupsItem( QgsDataItem *parent, const QString &path, const QString &authcfg, const QgsStringMap &headers, const QString &communityEndpoint, const QString &contentEndpoint )
251 : QgsDataCollectionItem( parent, tr( "Groups" ), path, QStringLiteral( "AFS" ) )
252 , mAuthCfg( authcfg )
253 , mHeaders( headers )
254 , mPortalCommunityEndpoint( communityEndpoint )
255 , mPortalContentEndpoint( contentEndpoint )
256 {
257 mIconName = QStringLiteral( "mIconDbSchema.svg" );
258 mCapabilities |= Qgis::BrowserItemCapability::Collapse;
259 setToolTip( path );
260 }
261
262
createChildren()263 QVector<QgsDataItem *> QgsArcGisPortalGroupsItem::createChildren()
264 {
265 QVector<QgsDataItem *> items;
266
267 QString errorTitle;
268 QString errorMessage;
269 const QVariantList groups = QgsArcGisPortalUtils::retrieveUserGroups( mPortalCommunityEndpoint, QString(), mAuthCfg, errorTitle, errorMessage, mHeaders );
270 if ( groups.isEmpty() )
271 {
272 if ( !errorMessage.isEmpty() )
273 {
274 std::unique_ptr< QgsErrorItem > error = std::make_unique< QgsErrorItem >( this, tr( "Connection failed: %1" ).arg( errorTitle ), mPath + "/error" );
275 error->setToolTip( errorMessage );
276 items.append( error.release() );
277 QgsDebugMsg( "Connection failed - " + errorMessage );
278 }
279 return items;
280 }
281
282 for ( const QVariant &group : groups )
283 {
284 const QVariantMap groupData = group.toMap();
285 items << new QgsArcGisPortalGroupItem( this, groupData.value( QStringLiteral( "id" ) ).toString(),
286 groupData.value( QStringLiteral( "title" ) ).toString(),
287 mAuthCfg, mHeaders, mPortalCommunityEndpoint, mPortalContentEndpoint );
288 items.last()->setToolTip( groupData.value( QStringLiteral( "snippet" ) ).toString() );
289 }
290
291 return items;
292 }
293
equal(const QgsDataItem * other)294 bool QgsArcGisPortalGroupsItem::equal( const QgsDataItem *other )
295 {
296 const QgsArcGisPortalGroupsItem *o = qobject_cast<const QgsArcGisPortalGroupsItem *>( other );
297 return ( type() == other->type() && o && mPath == o->mPath && mName == o->mName );
298 }
299
300
301 //
302 // QgsArcGisPortalGroupItem
303 //
QgsArcGisPortalGroupItem(QgsDataItem * parent,const QString & groupId,const QString & name,const QString & authcfg,const QgsStringMap & headers,const QString & communityEndpoint,const QString & contentEndpoint)304 QgsArcGisPortalGroupItem::QgsArcGisPortalGroupItem( QgsDataItem *parent, const QString &groupId, const QString &name, const QString &authcfg, const QgsStringMap &headers, const QString &communityEndpoint, const QString &contentEndpoint )
305 : QgsDataCollectionItem( parent, name, groupId, QStringLiteral( "AFS" ) )
306 , mId( groupId )
307 , mAuthCfg( authcfg )
308 , mHeaders( headers )
309 , mPortalCommunityEndpoint( communityEndpoint )
310 , mPortalContentEndpoint( contentEndpoint )
311 {
312 mIconName = QStringLiteral( "mIconDbSchema.svg" );
313 mCapabilities |= Qgis::BrowserItemCapability::Collapse;
314 setToolTip( name );
315 }
316
createChildren()317 QVector<QgsDataItem *> QgsArcGisPortalGroupItem::createChildren()
318 {
319 QVector<QgsDataItem *> items;
320
321 QString errorTitle;
322 QString errorMessage;
323 const QVariantList groupItems = QgsArcGisPortalUtils::retrieveGroupItemsOfType( mPortalContentEndpoint, mId, mAuthCfg, QList<int >() << QgsArcGisPortalUtils::FeatureService
324 << QgsArcGisPortalUtils::MapService
325 << QgsArcGisPortalUtils::ImageService,
326 errorTitle, errorMessage, mHeaders );
327 if ( groupItems.isEmpty() )
328 {
329 if ( !errorMessage.isEmpty() )
330 {
331 std::unique_ptr< QgsErrorItem > error = std::make_unique< QgsErrorItem >( this, tr( "Connection failed: %1" ).arg( errorTitle ), mPath + "/error" );
332 error->setToolTip( errorMessage );
333 items.append( error.release() );
334 QgsDebugMsg( "Connection failed - " + errorMessage );
335 }
336 return items;
337 }
338
339 for ( const QVariant &item : groupItems )
340 {
341 const QVariantMap itemData = item.toMap();
342
343 if ( itemData.value( QStringLiteral( "type" ) ).toString().compare( QStringLiteral( "Feature Service" ), Qt::CaseInsensitive ) == 0 )
344 {
345 items << new QgsArcGisFeatureServiceItem( this, itemData.value( QStringLiteral( "title" ) ).toString(),
346 itemData.value( QStringLiteral( "url" ) ).toString(),
347 itemData.value( QStringLiteral( "url" ) ).toString(), mAuthCfg, mHeaders );
348 }
349 else
350 {
351 items << new QgsArcGisMapServiceItem( this, itemData.value( QStringLiteral( "title" ) ).toString(),
352 itemData.value( QStringLiteral( "url" ) ).toString(),
353 itemData.value( QStringLiteral( "url" ) ).toString(), mAuthCfg, mHeaders, itemData.value( QStringLiteral( "type" ) ).toString().compare( QStringLiteral( "Map Service" ), Qt::CaseInsensitive ) == 0 ? QStringLiteral( "MapServer" ) : QStringLiteral( "ImageServer" ) );
354 }
355 }
356
357 return items;
358 }
359
equal(const QgsDataItem * other)360 bool QgsArcGisPortalGroupItem::equal( const QgsDataItem *other )
361 {
362 const QgsArcGisPortalGroupItem *o = qobject_cast<const QgsArcGisPortalGroupItem *>( other );
363 return ( type() == other->type() && o && mId == o->mId && mName == o->mName );
364 }
365
366
367 //
368 // QgsArcGisRestServicesItem
369 //
370
QgsArcGisRestServicesItem(QgsDataItem * parent,const QString & url,const QString & path,const QString & authcfg,const QgsStringMap & headers)371 QgsArcGisRestServicesItem::QgsArcGisRestServicesItem( QgsDataItem *parent, const QString &url, const QString &path, const QString &authcfg, const QgsStringMap &headers )
372 : QgsDataCollectionItem( parent, tr( "Services" ), path, QStringLiteral( "AFS" ) )
373 , mUrl( url )
374 , mAuthCfg( authcfg )
375 , mHeaders( headers )
376 {
377 mIconName = QStringLiteral( "mIconDbSchema.svg" );
378 mCapabilities |= Qgis::BrowserItemCapability::Collapse;
379 }
380
createChildren()381 QVector<QgsDataItem *> QgsArcGisRestServicesItem::createChildren()
382 {
383 QVector<QgsDataItem *> items;
384 QString errorTitle, errorMessage;
385 const QVariantMap serviceData = QgsArcGisRestQueryUtils::getServiceInfo( mUrl, mAuthCfg, errorTitle, errorMessage, mHeaders );
386 if ( serviceData.isEmpty() )
387 {
388 if ( !errorMessage.isEmpty() )
389 {
390 std::unique_ptr< QgsErrorItem > error = std::make_unique< QgsErrorItem >( this, tr( "Connection failed: %1" ).arg( errorTitle ), mPath + "/error" );
391 error->setToolTip( errorMessage );
392 items.append( error.release() );
393 QgsDebugMsg( "Connection failed - " + errorMessage );
394 }
395 return items;
396 }
397
398 addFolderItems( items, serviceData, mUrl, mAuthCfg, mHeaders, this, QString() );
399 addServiceItems( items, serviceData, mUrl, mAuthCfg, mHeaders, this, QString() );
400 addLayerItems( items, serviceData, mUrl, mAuthCfg, mHeaders, this, QgsArcGisRestQueryUtils::AllTypes, QString() );
401 return items;
402 }
403
equal(const QgsDataItem * other)404 bool QgsArcGisRestServicesItem::equal( const QgsDataItem *other )
405 {
406 const QgsArcGisRestServicesItem *o = qobject_cast<const QgsArcGisRestServicesItem *>( other );
407 return ( type() == other->type() && o && mPath == o->mPath && mName == o->mName );
408 }
409
410
411
412 //
413 // QgsArcGisRestFolderItem
414 //
QgsArcGisRestFolderItem(QgsDataItem * parent,const QString & name,const QString & path,const QString & baseUrl,const QString & authcfg,const QgsStringMap & headers)415 QgsArcGisRestFolderItem::QgsArcGisRestFolderItem( QgsDataItem *parent, const QString &name, const QString &path, const QString &baseUrl, const QString &authcfg, const QgsStringMap &headers )
416 : QgsDataCollectionItem( parent, name, path, QStringLiteral( "AFS" ) )
417 , mBaseUrl( baseUrl )
418 , mAuthCfg( authcfg )
419 , mHeaders( headers )
420 {
421 mIconName = QStringLiteral( "mIconDbSchema.svg" );
422 mCapabilities |= Qgis::BrowserItemCapability::Collapse;
423 setToolTip( path );
424 }
425
setSupportedFormats(const QString & formats)426 void QgsArcGisRestFolderItem::setSupportedFormats( const QString &formats )
427 {
428 mSupportedFormats = formats;
429 }
430
431
createChildren()432 QVector<QgsDataItem *> QgsArcGisRestFolderItem::createChildren()
433 {
434 const QString url = mPath;
435
436 QVector<QgsDataItem *> items;
437 QString errorTitle, errorMessage;
438 const QVariantMap serviceData = QgsArcGisRestQueryUtils::getServiceInfo( url, mAuthCfg, errorTitle, errorMessage, mHeaders );
439 if ( serviceData.isEmpty() )
440 {
441 if ( !errorMessage.isEmpty() )
442 {
443 std::unique_ptr< QgsErrorItem > error = std::make_unique< QgsErrorItem >( this, tr( "Connection failed: %1" ).arg( errorTitle ), mPath + "/error" );
444 error->setToolTip( errorMessage );
445 items.append( error.release() );
446 QgsDebugMsg( "Connection failed - " + errorMessage );
447 }
448 return items;
449 }
450
451 addFolderItems( items, serviceData, mBaseUrl, mAuthCfg, mHeaders, this, mSupportedFormats );
452 addServiceItems( items, serviceData, mBaseUrl, mAuthCfg, mHeaders, this, mSupportedFormats );
453 addLayerItems( items, serviceData, mPath, mAuthCfg, mHeaders, this, QgsArcGisRestQueryUtils::Vector, mSupportedFormats );
454 return items;
455 }
456
equal(const QgsDataItem * other)457 bool QgsArcGisRestFolderItem::equal( const QgsDataItem *other )
458 {
459 const QgsArcGisRestFolderItem *o = qobject_cast<const QgsArcGisRestFolderItem *>( other );
460 return ( type() == other->type() && o && mPath == o->mPath && mName == o->mName );
461 }
462
463 //
464 // QgsArcGisFeatureServiceItem
465 //
QgsArcGisFeatureServiceItem(QgsDataItem * parent,const QString & name,const QString & path,const QString & baseUrl,const QString & authcfg,const QgsStringMap & headers)466 QgsArcGisFeatureServiceItem::QgsArcGisFeatureServiceItem( QgsDataItem *parent, const QString &name, const QString &path, const QString &baseUrl, const QString &authcfg, const QgsStringMap &headers )
467 : QgsDataCollectionItem( parent, name, path, QStringLiteral( "AFS" ) )
468 , mBaseUrl( baseUrl )
469 , mAuthCfg( authcfg )
470 , mHeaders( headers )
471 {
472 mIconName = QStringLiteral( "mIconDbSchema.svg" );
473 mCapabilities |= Qgis::BrowserItemCapability::Collapse;
474 setToolTip( path );
475 }
476
setSupportedFormats(const QString & formats)477 void QgsArcGisFeatureServiceItem::setSupportedFormats( const QString &formats )
478 {
479 mSupportedFormats = formats;
480 }
481
createChildren()482 QVector<QgsDataItem *> QgsArcGisFeatureServiceItem::createChildren()
483 {
484 const QString url = mPath;
485
486 QVector<QgsDataItem *> items;
487 QString errorTitle, errorMessage;
488 const QVariantMap serviceData = QgsArcGisRestQueryUtils::getServiceInfo( url, mAuthCfg, errorTitle, errorMessage, mHeaders );
489 if ( serviceData.isEmpty() )
490 {
491 if ( !errorMessage.isEmpty() )
492 {
493 std::unique_ptr< QgsErrorItem > error = std::make_unique< QgsErrorItem >( this, tr( "Connection failed: %1" ).arg( errorTitle ), mPath + "/error" );
494 error->setToolTip( errorMessage );
495 items.append( error.release() );
496 QgsDebugMsg( "Connection failed - " + errorMessage );
497 }
498 return items;
499 }
500
501 addFolderItems( items, serviceData, mBaseUrl, mAuthCfg, mHeaders, this, mSupportedFormats );
502 addServiceItems( items, serviceData, mBaseUrl, mAuthCfg, mHeaders, this, mSupportedFormats );
503 addLayerItems( items, serviceData, mPath, mAuthCfg, mHeaders, this, QgsArcGisRestQueryUtils::Vector, mSupportedFormats );
504 return items;
505 }
506
equal(const QgsDataItem * other)507 bool QgsArcGisFeatureServiceItem::equal( const QgsDataItem *other )
508 {
509 const QgsArcGisFeatureServiceItem *o = qobject_cast<const QgsArcGisFeatureServiceItem *>( other );
510 return ( type() == other->type() && o && mPath == o->mPath && mName == o->mName );
511 }
512
513
514 //
515 // QgsArcGisMapServiceItem
516 //
517
QgsArcGisMapServiceItem(QgsDataItem * parent,const QString & name,const QString & path,const QString & baseUrl,const QString & authcfg,const QgsStringMap & headers,const QString & serviceType)518 QgsArcGisMapServiceItem::QgsArcGisMapServiceItem( QgsDataItem *parent, const QString &name, const QString &path, const QString &baseUrl, const QString &authcfg, const QgsStringMap &headers, const QString &serviceType )
519 : QgsDataCollectionItem( parent, name, path, QStringLiteral( "AMS" ) )
520 , mBaseUrl( baseUrl )
521 , mAuthCfg( authcfg )
522 , mHeaders( headers )
523 , mServiceType( serviceType )
524 {
525 mIconName = QStringLiteral( "mIconDbSchema.svg" );
526 mCapabilities |= Qgis::BrowserItemCapability::Collapse;
527 setToolTip( path );
528 }
529
createChildren()530 QVector<QgsDataItem *> QgsArcGisMapServiceItem::createChildren()
531 {
532 const QString url = mPath;
533
534 QVector<QgsDataItem *> items;
535 QString errorTitle, errorMessage;
536 const QVariantMap serviceData = QgsArcGisRestQueryUtils::getServiceInfo( url, mAuthCfg, errorTitle, errorMessage, mHeaders );
537 if ( serviceData.isEmpty() )
538 {
539 if ( !errorMessage.isEmpty() )
540 {
541 std::unique_ptr< QgsErrorItem > error = std::make_unique< QgsErrorItem >( this, tr( "Connection failed: %1" ).arg( errorTitle ), mPath + "/error" );
542 error->setToolTip( errorMessage );
543 items.append( error.release() );
544 QgsDebugMsg( "Connection failed - " + errorMessage );
545 }
546 return items;
547 }
548
549 const QString supportedFormats = mServiceType == QLatin1String( "ImageServer" ) ?
550 QStringLiteral( "JPGPNG,PNG,PNG8,PNG24,JPG,BMP,GIF,TIFF,PNG32,BIP,BSQ,LERC" ) // ImageServer supported formats
551 : serviceData.value( QStringLiteral( "supportedImageFormatTypes" ) ).toString();
552
553 addFolderItems( items, serviceData, mBaseUrl, mAuthCfg, mHeaders, this, supportedFormats );
554 addServiceItems( items, serviceData, mBaseUrl, mAuthCfg, mHeaders, this, supportedFormats );
555 addLayerItems( items, serviceData, mPath, mAuthCfg, mHeaders, this, QgsArcGisRestQueryUtils::AllTypes, supportedFormats );
556 return items;
557 }
558
equal(const QgsDataItem * other)559 bool QgsArcGisMapServiceItem::equal( const QgsDataItem *other )
560 {
561 const QgsArcGisMapServiceItem *o = qobject_cast<const QgsArcGisMapServiceItem *>( other );
562 return ( type() == other->type() && o && mPath == o->mPath && mName == o->mName );
563 }
564
565
566 //
567 // QgsArcGisFeatureServiceLayerItem
568 //
569
QgsArcGisFeatureServiceLayerItem(QgsDataItem * parent,const QString &,const QString & url,const QString & title,const QString & authid,const QString & authcfg,const QgsStringMap & headers,Qgis::BrowserLayerType geometryType)570 QgsArcGisFeatureServiceLayerItem::QgsArcGisFeatureServiceLayerItem( QgsDataItem *parent, const QString &, const QString &url, const QString &title, const QString &authid, const QString &authcfg, const QgsStringMap &headers, Qgis::BrowserLayerType geometryType )
571 : QgsLayerItem( parent, title, url, QString(), geometryType, QStringLiteral( "arcgisfeatureserver" ) )
572 {
573 mUri = QStringLiteral( "crs='%1' url='%2'" ).arg( authid, url );
574 if ( !authcfg.isEmpty() )
575 mUri += QStringLiteral( " authcfg='%1'" ).arg( authcfg );
576 if ( !headers.value( QStringLiteral( "Referer" ) ).isEmpty() )
577 mUri += QStringLiteral( " referer='%1'" ).arg( headers.value( QStringLiteral( "Referer" ) ) );
578 setState( Qgis::BrowserItemState::Populated );
579 setToolTip( url );
580 }
581
582 //
583 // QgsArcGisMapServiceLayerItem
584 //
585
QgsArcGisMapServiceLayerItem(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)586 QgsArcGisMapServiceLayerItem::QgsArcGisMapServiceLayerItem( 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 )
587 : QgsLayerItem( parent, title, url, QString(), Qgis::BrowserLayerType::Raster, QStringLiteral( "arcgismapserver" ) )
588 {
589 const QString trimmedUrl = id.isEmpty() ? url : url.left( url.length() - 1 - id.length() ); // trim '/0' from end of url -- AMS provider requires this omitted
590 mUri = QStringLiteral( "crs='%1' format='%2' layer='%3' url='%4'" ).arg( authid, format, id, trimmedUrl );
591 if ( !authcfg.isEmpty() )
592 mUri += QStringLiteral( " authcfg='%1'" ).arg( authcfg );
593 if ( !headers.value( QStringLiteral( "Referer" ) ).isEmpty() )
594 mUri += QStringLiteral( " referer='%1'" ).arg( headers.value( QStringLiteral( "Referer" ) ) );
595 setState( Qgis::BrowserItemState::Populated );
596 setToolTip( mPath );
597 }
598
599 //
600 // QgsArcGisRestParentLayerItem
601 //
602
QgsArcGisRestParentLayerItem(QgsDataItem * parent,const QString & name,const QString & path,const QString & authcfg,const QgsStringMap & headers)603 QgsArcGisRestParentLayerItem::QgsArcGisRestParentLayerItem( QgsDataItem *parent, const QString &name, const QString &path, const QString &authcfg, const QgsStringMap &headers )
604 : QgsDataItem( Qgis::BrowserItemType::Collection, parent, name, path )
605 , mAuthCfg( authcfg )
606 , mHeaders( headers )
607 {
608 mCapabilities |= Qgis::BrowserItemCapability::Fast;
609 mIconName = QStringLiteral( "mIconDbSchema.svg" );
610 setToolTip( path );
611 }
612
equal(const QgsDataItem * other)613 bool QgsArcGisRestParentLayerItem::equal( const QgsDataItem *other )
614 {
615 const QgsArcGisRestParentLayerItem *o = qobject_cast<const QgsArcGisRestParentLayerItem *>( other );
616 return ( type() == other->type() && o && mPath == o->mPath && mName == o->mName );
617 }
618
619
620 //
621 // QgsArcGisRestDataItemProvider
622 //
623
QgsArcGisRestDataItemProvider()624 QgsArcGisRestDataItemProvider::QgsArcGisRestDataItemProvider()
625 {
626 // migrate legacy map services by moving them to feature server group
627
628 QgsSettings settings;
629 settings.beginGroup( QStringLiteral( "qgis/connections-arcgismapserver" ) );
630 const QStringList legacyServices = settings.childGroups();
631 settings.endGroup();
632 settings.beginGroup( QStringLiteral( "qgis/connections-arcgisfeatureserver" ) );
633 QStringList existingServices = settings.childGroups();
634 settings.endGroup();
635 for ( const QString &legacyService : legacyServices )
636 {
637 QString newName = legacyService;
638 int i = 1;
639 while ( existingServices.contains( newName ) )
640 {
641 i ++;
642 newName = QStringLiteral( "%1 (%2)" ).arg( legacyService ).arg( i );
643 }
644
645 settings.beginGroup( QStringLiteral( "qgis/connections-arcgismapserver/%1" ).arg( legacyService ) );
646 const QStringList keys = settings.childKeys();
647 settings.endGroup();
648 for ( const QString &key : keys )
649 {
650 const QString oldKey = QStringLiteral( "qgis/connections-arcgismapserver/%1/%2" ).arg( legacyService, key );
651 const QString newKey = QStringLiteral( "qgis/connections-arcgisfeatureserver/%1/%2" ).arg( newName, key );
652 settings.setValue( newKey, settings.value( oldKey ) );
653 }
654
655 settings.remove( QStringLiteral( "qgis/connections-arcgismapserver/%1" ).arg( legacyService ) );
656 existingServices.append( newName );
657 }
658 }
659
name()660 QString QgsArcGisRestDataItemProvider::name()
661 {
662 return QStringLiteral( "AFS" );
663 }
664
capabilities() const665 int QgsArcGisRestDataItemProvider::capabilities() const
666 {
667 return QgsDataProvider::Net;
668 }
669
createDataItem(const QString & path,QgsDataItem * parentItem)670 QgsDataItem *QgsArcGisRestDataItemProvider::createDataItem( const QString &path, QgsDataItem *parentItem )
671 {
672 if ( path.isEmpty() )
673 {
674 return new QgsArcGisRestRootItem( parentItem, QObject::tr( "ArcGIS REST Servers" ), QStringLiteral( "arcgisfeatureserver:" ) );
675 }
676
677 // path schema: afs:/connection name (used by OWS)
678 if ( path.startsWith( QLatin1String( "afs:/" ) ) )
679 {
680 const QString connectionName = path.split( '/' ).last();
681 if ( QgsOwsConnection::connectionList( QStringLiteral( "arcgisfeatureserver" ) ).contains( connectionName ) )
682 {
683 return new QgsArcGisRestConnectionItem( parentItem, QStringLiteral( "ArcGisFeatureServer" ), path, connectionName );
684 }
685 }
686
687 return nullptr;
688 }
689
690