QGIS API Documentation  3.27.0-Master (bef583a8ef)
qgswfstransaction.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgswfstransaction.cpp
3  -------------------------
4  begin : December 20 , 2016
5  copyright : (C) 2007 by Marco Hugentobler (original code)
6  (C) 2012 by RenĂ©-Luc D'Hont (original code)
7  (C) 2014 by Alessandro Pasotti (original code)
8  (C) 2017 by David Marteau
9  email : marco dot hugentobler at karto dot baug dot ethz dot ch
10  a dot pasotti at itopen dot it
11  david dot marteau at 3liz dot com
12  ***************************************************************************/
13 
14 /***************************************************************************
15  * *
16  * This program is free software; you can redistribute it and/or modify *
17  * it under the terms of the GNU General Public License as published by *
18  * the Free Software Foundation; either version 2 of the License, or *
19  * (at your option) any later version. *
20  * *
21  ***************************************************************************/
22 
23 
24 #include "qgswfsutils.h"
25 #include "qgsserverprojectutils.h"
26 #include "qgsserverfeatureid.h"
27 #include "qgsfields.h"
28 #include "qgsexpression.h"
29 #include "qgsgeometry.h"
30 #include "qgsmaplayer.h"
31 #include "qgsfeatureiterator.h"
32 #include "qgsvectordataprovider.h"
33 #include "qgsvectorlayer.h"
34 #include "qgsfilterrestorer.h"
35 #include "qgsogcutils.h"
36 #include "qgswfstransaction.h"
37 #include "qgsproject.h"
39 
40 #include "qgslogger.h"
41 #include "qgsserverlogger.h"
42 
43 
44 namespace QgsWfs
45 {
46  namespace
47  {
48  void addTransactionResult( QDomDocument &responseDoc, QDomElement &resultsElem,
49  const QString &locator, const QString &message );
50  }
51 
52 
53  void writeTransaction( QgsServerInterface *serverIface, const QgsProject *project,
54  const QString &version, const QgsServerRequest &request,
55  QgsServerResponse &response )
56 
57  {
58  QDomDocument doc = createTransactionDocument( serverIface, project, version, request );
59 
60  response.setHeader( "Content-Type", "text/xml; charset=utf-8" );
61  response.write( doc.toByteArray() );
62  }
63 
64  QDomDocument createTransactionDocument( QgsServerInterface *serverIface, const QgsProject *project,
65  const QString &version, const QgsServerRequest &request )
66  {
67  Q_UNUSED( version )
68 
69  QgsServerRequest::Parameters parameters = request.parameters();
70  transactionRequest aRequest;
71 
72  QDomDocument doc;
73  QString errorMsg;
74 
75  if ( doc.setContent( request.data(), true, &errorMsg ) )
76  {
77  QDomElement docElem = doc.documentElement();
78  aRequest = parseTransactionRequestBody( docElem, project );
79  }
80  else
81  {
82  aRequest = parseTransactionParameters( parameters, project );
83  }
84 
85  int actionCount = aRequest.inserts.size() + aRequest.updates.size() + aRequest.deletes.size();
86  if ( actionCount == 0 )
87  {
88  throw QgsRequestNotWellFormedException( QStringLiteral( "No actions found" ) );
89  }
90 
91  performTransaction( aRequest, serverIface, project );
92 
93  // It's time to make the transaction
94  // Create the response document
95  QDomDocument resp;
96  //wfs:TransactionRespone element
97  QDomElement respElem = resp.createElement( QStringLiteral( "TransactionResponse" )/*wfs:TransactionResponse*/ );
98  respElem.setAttribute( QStringLiteral( "xmlns" ), WFS_NAMESPACE );
99  respElem.setAttribute( QStringLiteral( "xmlns:xsi" ), QStringLiteral( "http://www.w3.org/2001/XMLSchema-instance" ) );
100  respElem.setAttribute( QStringLiteral( "xsi:schemaLocation" ), WFS_NAMESPACE + " http://schemas.opengis.net/wfs/1.1.0/wfs.xsd" );
101  respElem.setAttribute( QStringLiteral( "xmlns:ogc" ), OGC_NAMESPACE );
102  respElem.setAttribute( QStringLiteral( "version" ), QStringLiteral( "1.1.0" ) );
103  resp.appendChild( respElem );
104 
105  int totalInserted = 0;
106  int totalUpdated = 0;
107  int totalDeleted = 0;
108  int errorCount = 0;
109 
110  //wfs:TransactionResults element
111  QDomElement trsElem = doc.createElement( QStringLiteral( "TransactionResults" ) );
112 
113  //wfs:InsertResults element
114  QDomElement irsElem = doc.createElement( QStringLiteral( "InsertResults" ) );
115  QList<transactionInsert>::iterator tiIt = aRequest.inserts.begin();
116  for ( ; tiIt != aRequest.inserts.end(); ++tiIt )
117  {
118  transactionInsert &action = *tiIt;
119  if ( action.error )
120  {
121  errorCount += 1;
122  QString locator = action.handle;
123  if ( locator.isEmpty() )
124  {
125  locator = QStringLiteral( "Insert:%1" ).arg( action.typeName );
126  }
127  addTransactionResult( resp, trsElem, locator, action.errorMsg );
128  }
129  else
130  {
131  QStringList::const_iterator fidIt = action.insertFeatureIds.constBegin();
132  for ( ; fidIt != action.insertFeatureIds.constEnd(); ++fidIt )
133  {
134  QString fidStr = *fidIt;
135  QDomElement irElem = doc.createElement( QStringLiteral( "Feature" ) );
136  if ( !action.handle.isEmpty() )
137  {
138  irElem.setAttribute( QStringLiteral( "handle" ), action.handle );
139  }
140  QDomElement fiElem = doc.createElement( QStringLiteral( "ogc:FeatureId" ) );
141  fiElem.setAttribute( QStringLiteral( "fid" ), fidStr );
142  irElem.appendChild( fiElem );
143  irsElem.appendChild( irElem );
144  }
145  }
146  totalInserted += action.insertFeatureIds.count();
147  }
148 
149  QList<transactionUpdate>::iterator tuIt = aRequest.updates.begin();
150  for ( ; tuIt != aRequest.updates.end(); ++tuIt )
151  {
152  transactionUpdate &action = *tuIt;
153  if ( action.error )
154  {
155  errorCount += 1;
156  QString locator = action.handle;
157  if ( locator.isEmpty() )
158  {
159  locator = QStringLiteral( "Update:%1" ).arg( action.typeName );
160  }
161  addTransactionResult( resp, trsElem, locator, action.errorMsg );
162  }
163  totalUpdated += action.totalUpdated;
164  }
165 
166  QList<transactionDelete>::iterator tdIt = aRequest.deletes.begin();
167  for ( ; tdIt != aRequest.deletes.end(); ++tdIt )
168  {
169  transactionDelete &action = *tdIt;
170  if ( action.error )
171  {
172  errorCount += 1;
173  QString locator = action.handle;
174  if ( locator.isEmpty() )
175  {
176  locator = QStringLiteral( "Delete:%1" ).arg( action.typeName );
177  }
178  addTransactionResult( resp, trsElem, locator, action.errorMsg );
179  }
180  totalDeleted += action.totalDeleted;
181  }
182 
183  //wfs:TransactionSummary element
184  QDomElement summaryElem = doc.createElement( QStringLiteral( "TransactionSummary" ) );
185  if ( aRequest.inserts.size() > 0 )
186  {
187  QDomElement totalInsertedElem = doc.createElement( QStringLiteral( "totalInserted" ) );
188  totalInsertedElem.appendChild( doc.createTextNode( QString::number( totalInserted ) ) );
189  summaryElem.appendChild( totalInsertedElem );
190  }
191  if ( aRequest.updates.size() > 0 )
192  {
193  QDomElement totalUpdatedElem = doc.createElement( QStringLiteral( "totalUpdated" ) );
194  totalUpdatedElem.appendChild( doc.createTextNode( QString::number( totalUpdated ) ) );
195  summaryElem.appendChild( totalUpdatedElem );
196  }
197  if ( aRequest.deletes.size() > 0 )
198  {
199  QDomElement totalDeletedElem = doc.createElement( QStringLiteral( "totalDeleted" ) );
200  totalDeletedElem.appendChild( doc.createTextNode( QString::number( totalDeleted ) ) );
201  summaryElem.appendChild( totalDeletedElem );
202  }
203  respElem.appendChild( summaryElem );
204 
205  // add TransactionResults
206  if ( errorCount > 0 && trsElem.hasChildNodes() )
207  {
208  respElem.appendChild( trsElem );
209  }
210 
211  // add InsertResults
212  if ( aRequest.inserts.size() > 0 && irsElem.hasChildNodes() )
213  {
214  respElem.appendChild( irsElem );
215  }
216  return resp;
217  }
218 
219  void performTransaction( transactionRequest &aRequest, QgsServerInterface *serverIface, const QgsProject *project )
220  {
221 #ifndef HAVE_SERVER_PYTHON_PLUGINS
222  ( void )serverIface;
223 #endif
224  // store typeName
225  QStringList typeNameList;
226 
227  QList<transactionInsert>::iterator tiIt = aRequest.inserts.begin();
228  for ( ; tiIt != aRequest.inserts.end(); ++tiIt )
229  {
230  QString name = ( *tiIt ).typeName;
231  if ( !typeNameList.contains( name ) )
232  typeNameList << name;
233  }
234  QList<transactionUpdate>::iterator tuIt = aRequest.updates.begin();
235  for ( ; tuIt != aRequest.updates.end(); ++tuIt )
236  {
237  QString name = ( *tuIt ).typeName;
238  if ( !typeNameList.contains( name ) )
239  typeNameList << name;
240  }
241  QList<transactionDelete>::iterator tdIt = aRequest.deletes.begin();
242  for ( ; tdIt != aRequest.deletes.end(); ++tdIt )
243  {
244  QString name = ( *tdIt ).typeName;
245  if ( !typeNameList.contains( name ) )
246  typeNameList << name;
247  }
248 
249 #ifdef HAVE_SERVER_PYTHON_PLUGINS
250  // get access controls
251  QgsAccessControl *accessControl = serverIface->accessControls();
252 #endif
253 
254  //scoped pointer to restore all original layer filters (subsetStrings) when pointer goes out of scope
255  //there's LOTS of potential exit paths here, so we avoid having to restore the filters manually
256  std::unique_ptr< QgsOWSServerFilterRestorer > filterRestorer( new QgsOWSServerFilterRestorer() );
257 
258  // get layers
259  QStringList wfsLayerIds = QgsServerProjectUtils::wfsLayerIds( *project );
263  QMap<QString, QgsVectorLayer *> mapLayerMap;
264  for ( int i = 0; i < wfsLayerIds.size(); ++i )
265  {
266  QgsMapLayer *layer = project->mapLayer( wfsLayerIds.at( i ) );
267  if ( !layer )
268  {
269  continue;
270  }
271  if ( layer->type() != QgsMapLayerType::VectorLayer )
272  {
273  continue;
274  }
275 
276  QString name = layerTypeName( layer );
277 
278  if ( !typeNameList.contains( name ) )
279  {
280  continue;
281  }
282 
283  // get vector layer
284  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
285  if ( !vlayer )
286  {
287  throw QgsRequestNotWellFormedException( QStringLiteral( "Layer error on '%1'" ).arg( name ) );
288  }
289 
290  //get provider
291  QgsVectorDataProvider *provider = vlayer->dataProvider();
292  if ( !provider )
293  {
294  throw QgsRequestNotWellFormedException( QStringLiteral( "Provider error on layer '%1'" ).arg( name ) );
295  }
296 
297  // get provider capabilities
298  int cap = provider->capabilities();
301  {
302  throw QgsRequestNotWellFormedException( QStringLiteral( "No capabilities to do WFS changes on layer '%1'" ).arg( name ) );
303  }
304 
305  if ( !wfstUpdateLayerIds.contains( vlayer->id() )
306  && !wfstDeleteLayerIds.contains( vlayer->id() )
307  && !wfstInsertLayerIds.contains( vlayer->id() ) )
308  {
309  throw QgsSecurityAccessException( QStringLiteral( "No permissions to do WFS changes on layer '%1'" ).arg( name ) );
310  }
311 #ifdef HAVE_SERVER_PYTHON_PLUGINS
312  if ( accessControl && !accessControl->layerUpdatePermission( vlayer )
313  && !accessControl->layerDeletePermission( vlayer ) && !accessControl->layerInsertPermission( vlayer ) )
314  {
315  throw QgsSecurityAccessException( QStringLiteral( "No permissions to do WFS changes on layer '%1'" ).arg( name ) );
316  }
317 
318  if ( accessControl )
319  {
320  QgsOWSServerFilterRestorer::applyAccessControlLayerFilters( accessControl, vlayer, filterRestorer->originalFilters() );
321  }
322 #endif
323  // store layers
324  mapLayerMap[name] = vlayer;
325  }
326 
327  // perform updates
328  tuIt = aRequest.updates.begin();
329  for ( ; tuIt != aRequest.updates.end(); ++tuIt )
330  {
331  transactionUpdate &action = *tuIt;
332  QString typeName = action.typeName;
333 
334  if ( !mapLayerMap.keys().contains( typeName ) )
335  {
336  action.error = true;
337  action.errorMsg = QStringLiteral( "TypeName '%1' unknown" ).arg( typeName );
338  continue;
339  }
340 
341  // get vector layer
342  QgsVectorLayer *vlayer = mapLayerMap[typeName];
343 
344  // verifying specific permissions
345  if ( !wfstUpdateLayerIds.contains( vlayer->id() ) )
346  {
347  action.error = true;
348  action.errorMsg = QStringLiteral( "No permissions to do WFS updates on layer '%1'" ).arg( typeName );
349  continue;
350  }
351 #ifdef HAVE_SERVER_PYTHON_PLUGINS
352  if ( accessControl && !accessControl->layerUpdatePermission( vlayer ) )
353  {
354  action.error = true;
355  action.errorMsg = QStringLiteral( "No permissions to do WFS updates on layer '%1'" ).arg( typeName );
356  continue;
357  }
358 #endif
359  //get provider
360  QgsVectorDataProvider *provider = vlayer->dataProvider();
361 
362  // verifying specific capabilities
363  int cap = provider->capabilities();
365  {
366  action.error = true;
367  action.errorMsg = QStringLiteral( "No capabilities to do WFS updates on layer '%1'" ).arg( typeName );
368  continue;
369  }
370  // start editing
371  vlayer->startEditing();
372 
373  // update request
374  QgsFeatureRequest featureRequest = action.featureRequest;
375 
376  // expression context
377  QgsExpressionContext expressionContext;
378  expressionContext << QgsExpressionContextUtils::globalScope()
381  featureRequest.setExpressionContext( expressionContext );
382 
383  // verifying feature ids list
384  if ( !action.serverFids.isEmpty() )
385  {
386  // update request based on feature ids
387  QgsServerFeatureId::updateFeatureRequestFromServerFids( featureRequest, action.serverFids, provider );
388  }
389 
390 #ifdef HAVE_SERVER_PYTHON_PLUGINS
391  if ( accessControl )
392  {
393  accessControl->filterFeatures( vlayer, featureRequest );
394  }
395 #endif
396  // get iterator
397  QgsFeatureIterator fit = vlayer->getFeatures( featureRequest );
398  QgsFeature feature;
399  int totalUpdated = 0;
400  // get action properties
401  QMap<QString, QString> propertyMap = action.propertyMap;
402  QDomElement geometryElem = action.geometryElement;
403  // get field information
404  QgsFields fields = provider->fields();
405  const QMap<QString, int> fieldMap = provider->fieldNameMap();
406  QMap<QString, int>::const_iterator fieldMapIt;
407  QString fieldName;
408  bool conversionSuccess;
409  // Update the features
410  while ( fit.nextFeature( feature ) )
411  {
412 #ifdef HAVE_SERVER_PYTHON_PLUGINS
413  if ( accessControl && !accessControl->allowToEdit( vlayer, feature ) )
414  {
415  action.error = true;
416  action.errorMsg = QStringLiteral( "Feature modify permission denied on layer '%1'" ).arg( typeName );
417  vlayer->rollBack();
418  break;
419  }
420 #endif
421  QMap< QString, QString >::const_iterator it = propertyMap.constBegin();
422  for ( ; it != propertyMap.constEnd(); ++it )
423  {
424  fieldName = it.key();
425  fieldMapIt = fieldMap.find( fieldName );
426  if ( fieldMapIt == fieldMap.constEnd() )
427  {
428  continue;
429  }
430  QgsField field = fields.at( fieldMapIt.value() );
431  QVariant value = it.value();
432  if ( value.isNull() )
433  {
434  if ( field.constraints().constraints() & QgsFieldConstraints::Constraint::ConstraintNotNull )
435  {
436  action.error = true;
437  action.errorMsg = QStringLiteral( "NOT NULL constraint error on layer '%1', field '%2'" ).arg( typeName, field.name() );
438  vlayer->rollBack();
439  break;
440  }
441  }
442  else // Not NULL
443  {
444  if ( field.type() == QVariant::Type::Int )
445  {
446  value = it.value().toInt( &conversionSuccess );
447  if ( !conversionSuccess )
448  {
449  action.error = true;
450  action.errorMsg = QStringLiteral( "Property conversion error on layer '%1'" ).arg( typeName );
451  vlayer->rollBack();
452  break;
453  }
454  }
455  else if ( field.type() == QVariant::Type::Double )
456  {
457  value = it.value().toDouble( &conversionSuccess );
458  if ( !conversionSuccess )
459  {
460  action.error = true;
461  action.errorMsg = QStringLiteral( "Property conversion error on layer '%1'" ).arg( typeName );
462  vlayer->rollBack();
463  break;
464  }
465  }
466  else if ( field.type() == QVariant::Type::LongLong )
467  {
468  value = it.value().toLongLong( &conversionSuccess );
469  if ( !conversionSuccess )
470  {
471  action.error = true;
472  action.errorMsg = QStringLiteral( "Property conversion error on layer '%1'" ).arg( typeName );
473  vlayer->rollBack();
474  break;
475  }
476  }
477  }
478  vlayer->changeAttributeValue( feature.id(), fieldMapIt.value(), value );
479  }
480  if ( action.error )
481  {
482  break;
483  }
484 
485  if ( !geometryElem.isNull() )
486  {
487  QgsGeometry g = QgsOgcUtils::geometryFromGML( geometryElem );
488  if ( g.isNull() )
489  {
490  action.error = true;
491  action.errorMsg = QStringLiteral( "Geometry from GML error on layer '%1'" ).arg( typeName );
492  vlayer->rollBack();
493  break;
494  }
495  if ( !vlayer->changeGeometry( feature.id(), g ) )
496  {
497  action.error = true;
498  action.errorMsg = QStringLiteral( "Error in change geometry on layer '%1'" ).arg( typeName );
499  vlayer->rollBack();
500  break;
501  }
502  }
503  totalUpdated += 1;
504  }
505  if ( action.error )
506  {
507  continue;
508  }
509 #ifdef HAVE_SERVER_PYTHON_PLUGINS
510  // verifying changes
511  if ( accessControl )
512  {
513  fit = vlayer->getFeatures( featureRequest );
514  while ( fit.nextFeature( feature ) )
515  {
516  if ( accessControl && !accessControl->allowToEdit( vlayer, feature ) )
517  {
518  action.error = true;
519  action.errorMsg = QStringLiteral( "Feature modify permission denied on layer '%1'" ).arg( typeName );
520  vlayer->rollBack();
521  break;
522  }
523  }
524  }
525  if ( action.error )
526  {
527  continue;
528  }
529 #endif
530 
531  // Commit the changes of the update elements
532  if ( !vlayer->commitChanges() )
533  {
534  action.error = true;
535  action.errorMsg = QStringLiteral( "Error committing updates: %1" ).arg( vlayer->commitErrors().join( QLatin1String( "; " ) ) );
536  vlayer->rollBack();
537  continue;
538  }
539  // all the changes are OK!
540  action.totalUpdated = totalUpdated;
541  action.error = false;
542 
543  }
544 
545  // perform deletes
546  tdIt = aRequest.deletes.begin();
547  for ( ; tdIt != aRequest.deletes.end(); ++tdIt )
548  {
549  transactionDelete &action = *tdIt;
550  QString typeName = action.typeName;
551 
552  if ( !mapLayerMap.keys().contains( typeName ) )
553  {
554  action.error = true;
555  action.errorMsg = QStringLiteral( "TypeName '%1' unknown" ).arg( typeName );
556  continue;
557  }
558 
559  // get vector layer
560  QgsVectorLayer *vlayer = mapLayerMap[typeName];
561 
562  // verifying specific permissions
563  if ( !wfstDeleteLayerIds.contains( vlayer->id() ) )
564  {
565  action.error = true;
566  action.errorMsg = QStringLiteral( "No permissions to do WFS deletes on layer '%1'" ).arg( typeName );
567  continue;
568  }
569 #ifdef HAVE_SERVER_PYTHON_PLUGINS
570  if ( accessControl && !accessControl->layerDeletePermission( vlayer ) )
571  {
572  action.error = true;
573  action.errorMsg = QStringLiteral( "No permissions to do WFS deletes on layer '%1'" ).arg( typeName );
574  continue;
575  }
576 #endif
577  //get provider
578  QgsVectorDataProvider *provider = vlayer->dataProvider();
579 
580  // verifying specific capabilities
581  int cap = provider->capabilities();
582  if ( !( cap & QgsVectorDataProvider::DeleteFeatures ) )
583  {
584  action.error = true;
585  action.errorMsg = QStringLiteral( "No capabilities to do WFS deletes on layer '%1'" ).arg( typeName );
586  continue;
587  }
588  // start editing
589  vlayer->startEditing();
590 
591  // delete request
592  QgsFeatureRequest featureRequest = action.featureRequest;
593 
594  // expression context
595  QgsExpressionContext expressionContext;
596  expressionContext << QgsExpressionContextUtils::globalScope()
599  featureRequest.setExpressionContext( expressionContext );
600 
601  // verifying feature ids list
602  if ( action.serverFids.isEmpty() )
603  {
604  action.error = true;
605  action.errorMsg = QStringLiteral( "No feature ids to do WFS deletes on layer '%1'" ).arg( typeName );
606  continue;
607  }
608 
609  // update request based on feature ids
610  QgsServerFeatureId::updateFeatureRequestFromServerFids( featureRequest, action.serverFids, provider );
611 
612 #ifdef HAVE_SERVER_PYTHON_PLUGINS
613  if ( accessControl )
614  {
615  accessControl->filterFeatures( vlayer, featureRequest );
616  }
617 #endif
618 
619  // get iterator
620  QgsFeatureIterator fit = vlayer->getFeatures( featureRequest );
621  QgsFeature feature;
622  // get deleted fids
623  QgsFeatureIds fids;
624  while ( fit.nextFeature( feature ) )
625  {
626 #ifdef HAVE_SERVER_PYTHON_PLUGINS
627  if ( accessControl && !accessControl->allowToEdit( vlayer, feature ) )
628  {
629  action.error = true;
630  action.errorMsg = QStringLiteral( "Feature modify permission denied" );
631  vlayer->rollBack();
632  break;
633  }
634 #endif
635  fids << feature.id();
636  }
637  if ( action.error )
638  {
639  continue;
640  }
641  // delete features
642  if ( !vlayer->deleteFeatures( fids ) )
643  {
644  action.error = true;
645  action.errorMsg = QStringLiteral( "Delete features failed on layer '%1'" ).arg( typeName );
646  vlayer->rollBack();
647  continue;
648  }
649 
650  // Commit the changes of the update elements
651  if ( !vlayer->commitChanges() )
652  {
653  action.error = true;
654  action.errorMsg = QStringLiteral( "Error committing deletes: %1" ).arg( vlayer->commitErrors().join( QLatin1String( "; " ) ) );
655  vlayer->rollBack();
656  continue;
657  }
658  // all the changes are OK!
659  action.totalDeleted = fids.count();
660  action.error = false;
661  }
662 
663  // perform inserts
664  tiIt = aRequest.inserts.begin();
665  for ( ; tiIt != aRequest.inserts.end(); ++tiIt )
666  {
667  transactionInsert &action = *tiIt;
668  QString typeName = action.typeName;
669 
670  if ( !mapLayerMap.keys().contains( typeName ) )
671  {
672  action.error = true;
673  action.errorMsg = QStringLiteral( "TypeName '%1' unknown" ).arg( typeName );
674  continue;
675  }
676 
677  // get vector layer
678  QgsVectorLayer *vlayer = mapLayerMap[typeName];
679 
680  // verifying specific permissions
681  if ( !wfstInsertLayerIds.contains( vlayer->id() ) )
682  {
683  action.error = true;
684  action.errorMsg = QStringLiteral( "No permissions to do WFS inserts on layer '%1'" ).arg( typeName );
685  continue;
686  }
687 #ifdef HAVE_SERVER_PYTHON_PLUGINS
688  if ( accessControl && !accessControl->layerInsertPermission( vlayer ) )
689  {
690  action.error = true;
691  action.errorMsg = QStringLiteral( "No permissions to do WFS inserts on layer '%1'" ).arg( typeName );
692  continue;
693  }
694 #endif
695  //get provider
696  QgsVectorDataProvider *provider = vlayer->dataProvider();
697 
698  // verifying specific capabilities
699  int cap = provider->capabilities();
700  if ( !( cap & QgsVectorDataProvider::AddFeatures ) )
701  {
702  action.error = true;
703  action.errorMsg = QStringLiteral( "No capabilities to do WFS inserts on layer '%1'" ).arg( typeName );
704  continue;
705  }
706 
707  // start editing
708  vlayer->startEditing();
709 
710  // get inserting features
711  QgsFeatureList featureList;
712  try
713  {
714  featureList = featuresFromGML( action.featureNodeList, vlayer );
715  }
716  catch ( QgsOgcServiceException &ex )
717  {
718  action.error = true;
719  action.errorMsg = QStringLiteral( "%1 '%2'" ).arg( ex.message(), typeName );
720  continue;
721  }
722 
723  if ( featureList.empty() )
724  {
725  action.error = true;
726  action.errorMsg = QStringLiteral( "No features to insert in layer '%1'" ).arg( typeName );
727  continue;
728  }
729 
730 #ifdef HAVE_SERVER_PYTHON_PLUGINS
731  // control features
732  if ( accessControl )
733  {
734  QgsFeatureList::iterator featureIt = featureList.begin();
735  while ( featureIt != featureList.end() )
736  {
737  if ( !accessControl->allowToEdit( vlayer, *featureIt ) )
738  {
739  action.error = true;
740  action.errorMsg = QStringLiteral( "Feature modify permission denied on layer '%1'" ).arg( typeName );
741  vlayer->rollBack();
742  break;
743  }
744  featureIt++;
745  }
746  }
747 #endif
748  if ( action.error )
749  {
750  continue;
751  }
752 
753  // perform add features
754  if ( !provider->addFeatures( featureList ) )
755  {
756  action.error = true;
757  action.errorMsg = QStringLiteral( "Insert features failed on layer '%1'" ).arg( typeName );
758  if ( provider ->hasErrors() )
759  {
760  provider->clearErrors();
761  }
762  vlayer->rollBack();
763  continue;
764  }
765 
766  // Commit the changes of the update elements
767  if ( !vlayer->commitChanges() )
768  {
769  action.error = true;
770  action.errorMsg = QStringLiteral( "Error committing inserts: %1" ).arg( vlayer->commitErrors().join( QLatin1String( "; " ) ) );
771  vlayer->rollBack();
772  continue;
773  }
774  // all changes are OK!
775  action.error = false;
776 
777  // Get the Feature Ids of the inserted feature
778  QgsAttributeList pkAttributes = provider->pkAttributeIndexes();
779  for ( const QgsFeature &feat : std::as_const( featureList ) )
780  {
781  action.insertFeatureIds << QStringLiteral( "%1.%2" ).arg( typeName, QgsServerFeatureId::getServerFid( feat, pkAttributes ) );
782  }
783  }
784 
785  //force restoration of original layer filters
786  filterRestorer.reset();
787  }
788 
789  QgsFeatureList featuresFromGML( QDomNodeList featureNodeList, QgsVectorLayer *layer )
790  {
791  // Store the inserted features
792  QgsFeatureList featList;
793 
794  const QgsVectorDataProvider *provider { layer->dataProvider() };
795 
796  // Get Layer Field Information
797  QgsFields fields = provider->fields();
798  const QMap<QString, int> fieldMap = provider->fieldNameMap();
799  QMap<QString, int>::const_iterator fieldMapIt;
800 
801  for ( int i = 0; i < featureNodeList.count(); i++ )
802  {
803  QgsFeature feat( fields );
804 
805  QDomElement featureElem = featureNodeList.at( i ).toElement();
806  QDomNode currentAttributeChild = featureElem.firstChild();
807  bool conversionSuccess = true;
808 
809  while ( !currentAttributeChild.isNull() )
810  {
811  QDomElement currentAttributeElement = currentAttributeChild.toElement();
812  QString attrName = currentAttributeElement.localName();
813 
814  if ( attrName != QLatin1String( "boundedBy" ) )
815  {
816  if ( attrName != QLatin1String( "geometry" ) ) //a normal attribute
817  {
818  fieldMapIt = fieldMap.find( attrName );
819  if ( fieldMapIt == fieldMap.constEnd() )
820  {
821  continue;
822  }
823 
824  QgsField field = fields.at( fieldMapIt.value() );
825  QString attrValue = currentAttributeElement.text();
826  int attrType = field.type();
827 
828  QgsMessageLog::logMessage( QStringLiteral( "attr: name=%1 idx=%2 value=%3" ).arg( attrName ).arg( fieldMapIt.value() ).arg( attrValue ) );
829 
830  if ( attrType == QVariant::Int )
831  feat.setAttribute( fieldMapIt.value(), attrValue.toInt( &conversionSuccess ) );
832  else if ( attrType == QVariant::Double )
833  feat.setAttribute( fieldMapIt.value(), attrValue.toDouble( &conversionSuccess ) );
834  else
835  feat.setAttribute( fieldMapIt.value(), attrValue );
836 
837  if ( !conversionSuccess )
838  {
839  throw QgsRequestNotWellFormedException( QStringLiteral( "Property conversion error on layer insert" ) );
840  }
841  }
842  else //a geometry attribute
843  {
844  const QgsOgcUtils::Context context { layer, provider->transformContext() };
845  QgsGeometry g = QgsOgcUtils::geometryFromGML( currentAttributeElement, context );
846  if ( g.isNull() )
847  {
848  throw QgsRequestNotWellFormedException( QStringLiteral( "Geometry from GML error on layer insert" ) );
849  }
850  feat.setGeometry( g );
851  }
852  }
853  currentAttributeChild = currentAttributeChild.nextSibling();
854  }
855  // update feature list
856  featList << feat;
857  }
858  return featList;
859  }
860 
862  {
863  if ( !parameters.contains( QStringLiteral( "OPERATION" ) ) )
864  {
865  throw QgsRequestNotWellFormedException( QStringLiteral( "OPERATION parameter is mandatory" ) );
866  }
867  if ( parameters.value( QStringLiteral( "OPERATION" ) ).toUpper() != QLatin1String( "DELETE" ) )
868  {
869  throw QgsRequestNotWellFormedException( QStringLiteral( "Only DELETE value is defined for OPERATION parameter" ) );
870  }
871 
872  // Verifying parameters mutually exclusive
873  if ( ( parameters.contains( QStringLiteral( "FEATUREID" ) )
874  && ( parameters.contains( QStringLiteral( "FILTER" ) ) || parameters.contains( QStringLiteral( "BBOX" ) ) ) )
875  || ( parameters.contains( QStringLiteral( "FILTER" ) )
876  && ( parameters.contains( QStringLiteral( "FEATUREID" ) ) || parameters.contains( QStringLiteral( "BBOX" ) ) ) )
877  || ( parameters.contains( QStringLiteral( "BBOX" ) )
878  && ( parameters.contains( QStringLiteral( "FEATUREID" ) ) || parameters.contains( QStringLiteral( "FILTER" ) ) ) )
879  )
880  {
881  throw QgsRequestNotWellFormedException( QStringLiteral( "FEATUREID FILTER and BBOX parameters are mutually exclusive" ) );
882  }
883 
884  transactionRequest request;
885 
886  QStringList typeNameList;
887  // parse FEATUREID
888  if ( parameters.contains( QStringLiteral( "FEATUREID" ) ) )
889  {
890  QStringList fidList = parameters.value( QStringLiteral( "FEATUREID" ) ).split( ',' );
891 
892  QMap<QString, QStringList> fidsMap;
893 
894  QStringList::const_iterator fidIt = fidList.constBegin();
895  for ( ; fidIt != fidList.constEnd(); ++fidIt )
896  {
897  // Get FeatureID
898  QString fid = *fidIt;
899  fid = fid.trimmed();
900  // testing typename in the WFS featureID
901  if ( !fid.contains( '.' ) )
902  {
903  throw QgsRequestNotWellFormedException( QStringLiteral( "FEATUREID has to have TYPENAME in the values" ) );
904  }
905 
906  QString typeName = fid.section( '.', 0, 0 );
907  fid = fid.section( '.', 1, 1 );
908  if ( !typeNameList.contains( typeName ) )
909  {
910  typeNameList << typeName;
911  }
912 
913  QStringList fids;
914  if ( fidsMap.contains( typeName ) )
915  {
916  fids = fidsMap.value( typeName );
917  }
918  fids.append( fid );
919  fidsMap.insert( typeName, fids );
920  }
921 
922  QMap<QString, QStringList>::const_iterator fidsMapIt = fidsMap.constBegin();
923  while ( fidsMapIt != fidsMap.constEnd() )
924  {
925  transactionDelete action;
926  action.typeName = fidsMapIt.key();
927 
928  action.serverFids = fidsMapIt.value();
930 
931  request.deletes.append( action );
932  }
933  return request;
934  }
935 
936  if ( !parameters.contains( QStringLiteral( "TYPENAME" ) ) )
937  {
938  throw QgsRequestNotWellFormedException( QStringLiteral( "TYPENAME is mandatory except if FEATUREID is used" ) );
939  }
940 
941  typeNameList = parameters.value( QStringLiteral( "TYPENAME" ) ).split( ',' );
942 
943  // Create actions based on TypeName
944  QStringList::const_iterator typeNameIt = typeNameList.constBegin();
945  for ( ; typeNameIt != typeNameList.constEnd(); ++typeNameIt )
946  {
947  QString typeName = *typeNameIt;
948  typeName = typeName.trimmed();
949 
950  transactionDelete action;
951  action.typeName = typeName;
952 
953  request.deletes.append( action );
954  }
955 
956  // Manage extra parameter exp_filter
957  if ( parameters.contains( QStringLiteral( "EXP_FILTER" ) ) )
958  {
959  QString expFilterName = parameters.value( QStringLiteral( "EXP_FILTER" ) );
960  QStringList expFilterList;
961  QRegExp rx( "\\(([^()]+)\\)" );
962  if ( rx.indexIn( expFilterName, 0 ) == -1 )
963  {
964  expFilterList << expFilterName;
965  }
966  else
967  {
968  int pos = 0;
969  while ( ( pos = rx.indexIn( expFilterName, pos ) ) != -1 )
970  {
971  expFilterList << rx.cap( 1 );
972  pos += rx.matchedLength();
973  }
974  }
975 
976  // Verifying the 1:1 mapping between TYPENAME and EXP_FILTER but without exception
977  if ( request.deletes.size() == expFilterList.size() )
978  {
979  // set feature request filter expression based on filter element
980  QList<transactionDelete>::iterator dIt = request.deletes.begin();
981  QStringList::const_iterator expFilterIt = expFilterList.constBegin();
982  for ( ; dIt != request.deletes.end(); ++dIt )
983  {
984  transactionDelete &action = *dIt;
985  // Get Filter for this typeName
986  QString expFilter;
987  if ( expFilterIt != expFilterList.constEnd() )
988  {
989  expFilter = *expFilterIt;
990  }
991  std::shared_ptr<QgsExpression> filter( new QgsExpression( expFilter ) );
992  if ( filter )
993  {
994  if ( filter->hasParserError() )
995  {
996  QgsMessageLog::logMessage( filter->parserErrorString() );
997  }
998  else
999  {
1000  if ( filter->needsGeometry() )
1001  {
1003  }
1004  action.featureRequest.setFilterExpression( filter->expression() );
1005  }
1006  }
1007  }
1008  }
1009  else
1010  {
1011  QgsMessageLog::logMessage( "There has to be a 1:1 mapping between each element in a TYPENAME and the EXP_FILTER list" );
1012  }
1013  }
1014 
1015  if ( parameters.contains( QStringLiteral( "BBOX" ) ) )
1016  {
1017  // get bbox value
1018  QString bbox = parameters.value( QStringLiteral( "BBOX" ) );
1019  if ( bbox.isEmpty() )
1020  {
1021  throw QgsRequestNotWellFormedException( QStringLiteral( "BBOX parameter is empty" ) );
1022  }
1023 
1024  // get bbox corners
1025  QStringList corners = bbox.split( ',' );
1026  if ( corners.size() != 4 )
1027  {
1028  throw QgsRequestNotWellFormedException( QStringLiteral( "BBOX has to be composed of 4 elements: '%1'" ).arg( bbox ) );
1029  }
1030 
1031  // convert corners to double
1032  double d[4];
1033  bool ok;
1034  for ( int i = 0; i < 4; i++ )
1035  {
1036  corners[i].replace( ' ', '+' );
1037  d[i] = corners[i].toDouble( &ok );
1038  if ( !ok )
1039  {
1040  throw QgsRequestNotWellFormedException( QStringLiteral( "BBOX has to be composed of 4 double: '%1'" ).arg( bbox ) );
1041  }
1042  }
1043  // create extent
1044  QgsRectangle extent( d[0], d[1], d[2], d[3] );
1045 
1046  // set feature request filter rectangle
1047  QList<transactionDelete>::iterator dIt = request.deletes.begin();
1048  for ( ; dIt != request.deletes.end(); ++dIt )
1049  {
1050  transactionDelete &action = *dIt;
1051  action.featureRequest.setFilterRect( extent );
1052  }
1053  return request;
1054  }
1055  else if ( parameters.contains( QStringLiteral( "FILTER" ) ) )
1056  {
1057  QString filterName = parameters.value( QStringLiteral( "FILTER" ) );
1058  QStringList filterList;
1059  QRegExp rx( "\\(([^()]+)\\)" );
1060  if ( rx.indexIn( filterName, 0 ) == -1 )
1061  {
1062  filterList << filterName;
1063  }
1064  else
1065  {
1066  int pos = 0;
1067  while ( ( pos = rx.indexIn( filterName, pos ) ) != -1 )
1068  {
1069  filterList << rx.cap( 1 );
1070  pos += rx.matchedLength();
1071  }
1072  }
1073 
1074  // Verifying the 1:1 mapping between TYPENAME and FILTER
1075  if ( request.deletes.size() != filterList.size() )
1076  {
1077  throw QgsRequestNotWellFormedException( QStringLiteral( "There has to be a 1:1 mapping between each element in a TYPENAME and the FILTER list" ) );
1078  }
1079 
1080  // set feature request filter expression based on filter element
1081  QList<transactionDelete>::iterator dIt = request.deletes.begin();
1082  QStringList::const_iterator filterIt = filterList.constBegin();
1083  for ( ; dIt != request.deletes.end(); ++dIt )
1084  {
1085  transactionDelete &action = *dIt;
1086 
1087  // Get Filter for this typeName
1088  QDomDocument filter;
1089  if ( filterIt != filterList.constEnd() )
1090  {
1091  QString errorMsg;
1092  if ( !filter.setContent( *filterIt, true, &errorMsg ) )
1093  {
1094  throw QgsRequestNotWellFormedException( QStringLiteral( "error message: %1. The XML string was: %2" ).arg( errorMsg, *filterIt ) );
1095  }
1096  }
1097 
1098  QDomElement filterElem = filter.firstChildElement();
1099  QStringList serverFids;
1100  action.featureRequest = parseFilterElement( action.typeName, filterElem, serverFids, project );
1101  action.serverFids = serverFids;
1102 
1103  if ( filterIt != filterList.constEnd() )
1104  {
1105  ++filterIt;
1106  }
1107  }
1108  return request;
1109  }
1110 
1111  return request;
1112  }
1113 
1114  transactionRequest parseTransactionRequestBody( QDomElement &docElem, const QgsProject *project )
1115  {
1116  transactionRequest request;
1117 
1118  QDomNodeList docChildNodes = docElem.childNodes();
1119 
1120  QDomElement actionElem;
1121  QString actionName;
1122 
1123  for ( int i = docChildNodes.count(); 0 < i; --i )
1124  {
1125  actionElem = docChildNodes.at( i - 1 ).toElement();
1126  actionName = actionElem.localName();
1127 
1128  if ( actionName == QLatin1String( "Insert" ) )
1129  {
1130  transactionInsert action = parseInsertActionElement( actionElem );
1131  request.inserts.append( action );
1132  }
1133  else if ( actionName == QLatin1String( "Update" ) )
1134  {
1135  transactionUpdate action = parseUpdateActionElement( actionElem, project );
1136  request.updates.append( action );
1137  }
1138  else if ( actionName == QLatin1String( "Delete" ) )
1139  {
1140  transactionDelete action = parseDeleteActionElement( actionElem, project );
1141  request.deletes.append( action );
1142  }
1143  }
1144 
1145  return request;
1146  }
1147 
1148  transactionDelete parseDeleteActionElement( QDomElement &actionElem, const QgsProject *project )
1149  {
1150  QString typeName = actionElem.attribute( QStringLiteral( "typeName" ) );
1151  if ( typeName.contains( ':' ) )
1152  typeName = typeName.section( ':', 1, 1 );
1153 
1154  QDomElement filterElem = actionElem.firstChild().toElement();
1155  if ( filterElem.tagName() != QLatin1String( "Filter" ) )
1156  {
1157  throw QgsRequestNotWellFormedException( QStringLiteral( "Delete action element first child is not Filter" ) );
1158  }
1159 
1160  QStringList serverFids;
1161  QgsFeatureRequest featureRequest = parseFilterElement( typeName, filterElem, serverFids, project );
1162 
1163  transactionDelete action;
1164  action.typeName = typeName;
1165  action.featureRequest = featureRequest;
1166  action.serverFids = serverFids;
1167  action.error = false;
1168 
1169  if ( actionElem.hasAttribute( QStringLiteral( "handle" ) ) )
1170  {
1171  action.handle = actionElem.attribute( QStringLiteral( "handle" ) );
1172  }
1173 
1174  return action;
1175  }
1176 
1177  transactionUpdate parseUpdateActionElement( QDomElement &actionElem, const QgsProject *project )
1178  {
1179  QgsMessageLog::logMessage( QStringLiteral( "parseUpdateActionElement" ), QStringLiteral( "Server" ), Qgis::MessageLevel::Info );
1180  QString typeName = actionElem.attribute( QStringLiteral( "typeName" ) );
1181  if ( typeName.contains( ':' ) )
1182  typeName = typeName.section( ':', 1, 1 );
1183 
1184  QDomNodeList propertyNodeList = actionElem.elementsByTagName( QStringLiteral( "Property" ) );
1185  if ( propertyNodeList.isEmpty() )
1186  {
1187  throw QgsRequestNotWellFormedException( QStringLiteral( "Update action element must have one or more Property element" ) );
1188  }
1189 
1190  QMap<QString, QString> propertyMap;
1191  QDomElement propertyElem;
1192  QDomElement nameElem;
1193  QDomElement valueElem;
1194  QDomElement geometryElem;
1195 
1196  for ( int l = 0; l < propertyNodeList.count(); ++l )
1197  {
1198  propertyElem = propertyNodeList.at( l ).toElement();
1199  nameElem = propertyElem.elementsByTagName( QStringLiteral( "Name" ) ).at( 0 ).toElement();
1200  valueElem = propertyElem.elementsByTagName( QStringLiteral( "Value" ) ).at( 0 ).toElement();
1201  if ( nameElem.text() != QLatin1String( "geometry" ) )
1202  {
1203  propertyMap.insert( nameElem.text(), valueElem.text() );
1204  }
1205  else
1206  {
1207  geometryElem = valueElem;
1208  }
1209  }
1210 
1211  QDomNodeList filterNodeList = actionElem.elementsByTagName( QStringLiteral( "Filter" ) );
1212  QgsFeatureRequest featureRequest;
1213  QStringList serverFids;
1214  if ( filterNodeList.size() != 0 )
1215  {
1216  QDomElement filterElem = filterNodeList.at( 0 ).toElement();
1217  featureRequest = parseFilterElement( typeName, filterElem, serverFids, project );
1218  }
1219  QgsMessageLog::logMessage( QStringLiteral( "parseUpdateActionElement: serverFids length %1" ).arg( serverFids.count() ), QStringLiteral( "Server" ), Qgis::MessageLevel::Info );
1220 
1221  transactionUpdate action;
1222  action.typeName = typeName;
1223  action.propertyMap = propertyMap;
1224  action.geometryElement = geometryElem;
1225  action.featureRequest = featureRequest;
1226  action.serverFids = serverFids;
1227  action.error = false;
1228 
1229  if ( actionElem.hasAttribute( QStringLiteral( "handle" ) ) )
1230  {
1231  action.handle = actionElem.attribute( QStringLiteral( "handle" ) );
1232  }
1233 
1234  return action;
1235  }
1236 
1237  transactionInsert parseInsertActionElement( QDomElement &actionElem )
1238  {
1239  QDomNodeList featureNodeList = actionElem.childNodes();
1240  if ( featureNodeList.size() != 1 )
1241  {
1242  throw QgsRequestNotWellFormedException( QStringLiteral( "Insert action element must have one or more child node" ) );
1243  }
1244 
1245  QString typeName;
1246  for ( int i = 0; i < featureNodeList.count(); ++i )
1247  {
1248  QString tempTypeName = featureNodeList.at( i ).toElement().localName();
1249  if ( tempTypeName.contains( ':' ) )
1250  tempTypeName = tempTypeName.section( ':', 1, 1 );
1251 
1252  if ( typeName.isEmpty() )
1253  {
1254  typeName = tempTypeName;
1255  }
1256  else if ( tempTypeName != typeName )
1257  {
1258  throw QgsRequestNotWellFormedException( QStringLiteral( "Insert action element must have one typename features" ) );
1259  }
1260  }
1261 
1262  transactionInsert action;
1263  action.typeName = typeName;
1264  action.featureNodeList = featureNodeList;
1265  action.error = false;
1266 
1267  if ( actionElem.hasAttribute( QStringLiteral( "handle" ) ) )
1268  {
1269  action.handle = actionElem.attribute( QStringLiteral( "handle" ) );
1270  }
1271 
1272  return action;
1273  }
1274 
1275  namespace
1276  {
1277 
1278  void addTransactionResult( QDomDocument &responseDoc, QDomElement &resultsElem,
1279  const QString &locator, const QString &message )
1280  {
1281  QDomElement trElem = responseDoc.createElement( QStringLiteral( "Action" ) );
1282  resultsElem.appendChild( trElem );
1283 
1284  if ( !locator.isEmpty() )
1285  {
1286  trElem.setAttribute( QStringLiteral( "locator" ), locator );
1287  }
1288 
1289  if ( !message.isEmpty() )
1290  {
1291  QDomElement mesElem = responseDoc.createElement( QStringLiteral( "Message" ) );
1292  mesElem.appendChild( responseDoc.createTextNode( message ) );
1293  trElem.appendChild( mesElem );
1294  }
1295  }
1296 
1297  }
1298 
1299 } // namespace QgsWfs
1300 
1301 
A helper class that centralizes restrictions given by all the access control filter plugins.
bool layerUpdatePermission(const QgsVectorLayer *layer) const
Returns the layer update right.
void filterFeatures(const QgsVectorLayer *layer, QgsFeatureRequest &filterFeatures) const override
Filter the features of the layer.
bool layerInsertPermission(const QgsVectorLayer *layer) const
Returns the layer insert right.
bool allowToEdit(const QgsVectorLayer *layer, const QgsFeature &feature) const
Are we authorized to modify the following geometry.
bool layerDeletePermission(const QgsVectorLayer *layer) const
Returns the layer delete right.
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
Class for parsing and evaluation of expressions (formerly called "search strings").
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
This class wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setFlags(QgsFeatureRequest::Flags flags)
Sets flags that affect how features will be fetched.
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
QgsFeatureRequest & setExpressionContext(const QgsExpressionContext &context)
Sets the expression context used to evaluate filter expressions.
QgsFeatureRequest & setFilterRect(const QgsRectangle &rectangle)
Sets the rectangle from which features will be taken.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
bool setAttribute(int field, const QVariant &attr)
Sets an attribute's value by field index.
Definition: qgsfeature.cpp:255
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
Definition: qgsfeature.cpp:163
Q_GADGET QgsFeatureId id
Definition: qgsfeature.h:64
Q_GADGET Constraints constraints
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:51
QString name
Definition: qgsfield.h:60
QVariant::Type type
Definition: qgsfield.h:58
QgsFieldConstraints constraints
Definition: qgsfield.h:63
Container of fields for a vector layer.
Definition: qgsfields.h:45
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
Definition: qgsfields.cpp:163
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:125
Q_GADGET bool isNull
Definition: qgsgeometry.h:127
Base class for all map layer types.
Definition: qgsmaplayer.h:73
QgsMapLayerType type
Definition: qgsmaplayer.h:80
QString id() const
Returns the layer's unique ID, which is used to access this layer from QgsProject.
QgsCoordinateTransformContext transformContext() const
Returns the layer data provider coordinate transform context or a default transform context if the la...
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
RAII class to restore layer filters on destruction.
static void applyAccessControlLayerFilters(const QgsAccessControl *accessControl, QgsMapLayer *mapLayer, QHash< QgsMapLayer *, QString > &originalLayerFilters)
Apply filter from AccessControl.
Exception base class for service exceptions.
QString message() const
Returns the exception message.
static QgsGeometry geometryFromGML(const QString &xmlString, const QgsOgcUtils::Context &context=QgsOgcUtils::Context())
Static method that creates geometry from GML.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition: qgsproject.h:104
Q_INVOKABLE QgsMapLayer * mapLayer(const QString &layerId) const
Retrieve a pointer to a registered layer by layer ID.
A rectangle specified with double values.
Definition: qgsrectangle.h:42
QgsServerInterface Class defining interfaces exposed by QGIS Server and made available to plugins.
virtual QgsAccessControl * accessControls() const =0
Gets the registered access control filters.
QgsServerRequest Class defining request interface passed to services QgsService::executeRequest() met...
QgsServerRequest::Parameters parameters() const
Returns a map of query parameters with keys converted to uppercase.
QMap< QString, QString > Parameters
virtual QByteArray data() const
Returns post/put data Check for QByteArray::isNull() to check if data is available.
QgsServerResponse Class defining response interface passed to services QgsService::executeRequest() m...
virtual void write(const QString &data)
Write string This is a convenient method that will write directly to the underlying I/O device.
virtual void setHeader(const QString &key, const QString &value)=0
Set Header entry Add Header entry to the response Note that it is usually an error to set Header afte...
This is the base class for vector data providers.
@ ChangeGeometries
Allows modifications of geometries.
@ DeleteFeatures
Allows deletion of features.
@ ChangeAttributeValues
Allows modification of attribute values.
@ AddFeatures
Allows adding features.
void clearErrors()
Clear recorded errors.
virtual QgsAttributeList pkAttributeIndexes() const
Returns list of indexes of fields that make up the primary key.
bool addFeatures(QgsFeatureList &flist, QgsFeatureSink::Flags flags=QgsFeatureSink::Flags()) override
Adds a list of features to the sink.
QgsFields fields() const override=0
Returns the fields associated with this data provider.
QMap< QString, int > fieldNameMap() const
Returns a map where the key is the name of the field and the value is its index.
virtual Q_INVOKABLE QgsVectorDataProvider::Capabilities capabilities() const
Returns flags containing the supported capabilities.
Represents a vector layer which manages a vector based data sets.
Q_INVOKABLE bool startEditing()
Makes the layer editable.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
bool deleteFeatures(const QgsFeatureIds &fids, DeleteContext *context=nullptr)
Deletes a set of features from the layer (but does not commit it)
QStringList commitErrors() const
Returns a list containing any error messages generated when attempting to commit changes to the layer...
Q_INVOKABLE bool rollBack(bool deleteBuffer=true)
Stops a current editing operation and discards any uncommitted edits.
Q_INVOKABLE bool commitChanges(bool stopEditing=true)
Attempts to commit to the underlying data provider any buffered changes made since the last to call t...
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer's data provider, it may be nullptr.
bool changeAttributeValue(QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue=QVariant(), bool skipDefaultValues=false)
Changes an attribute value for a feature (but does not immediately commit the changes).
bool changeGeometry(QgsFeatureId fid, QgsGeometry &geometry, bool skipDefaultValue=false)
Changes a feature's geometry within the layer's edit buffer (but does not immediately commit the chan...
Exception thrown in case of malformed request.
Exception thrown when data access violates access controls.
@ VectorLayer
Vector layer.
SERVER_EXPORT QgsFeatureRequest updateFeatureRequestFromServerFids(QgsFeatureRequest &featureRequest, const QStringList &serverFids, const QgsVectorDataProvider *provider)
Returns the feature request based on feature ids build with primary keys.
SERVER_EXPORT QString getServerFid(const QgsFeature &feature, const QgsAttributeList &pkAttributes)
Returns the feature id based on primary keys.
SERVER_EXPORT QStringList wfsLayerIds(const QgsProject &project)
Returns the Layer ids list defined in a QGIS project as published in WFS.
SERVER_EXPORT QStringList wfstUpdateLayerIds(const QgsProject &project)
Returns the Layer ids list defined in a QGIS project as published as WFS-T with update capabilities.
SERVER_EXPORT QStringList wfstInsertLayerIds(const QgsProject &project)
Returns the Layer ids list defined in a QGIS project as published as WFS-T with insert capabilities.
SERVER_EXPORT QStringList wfstDeleteLayerIds(const QgsProject &project)
Returns the Layer ids list defined in a QGIS project as published as WFS-T with delete capabilities.
WMS implementation.
Definition: qgswfs.cpp:36
QString layerTypeName(const QgsMapLayer *layer)
Returns typename from vector layer.
Definition: qgswfsutils.cpp:69
transactionRequest parseTransactionRequestBody(QDomElement &docElem, const QgsProject *project)
Transform RequestBody root element to getFeatureRequest.
const QString OGC_NAMESPACE
Definition: qgswfsutils.h:74
transactionRequest parseTransactionParameters(QgsServerRequest::Parameters parameters, const QgsProject *project)
const QString WFS_NAMESPACE
Definition: qgswfsutils.h:72
void performTransaction(transactionRequest &aRequest, QgsServerInterface *serverIface, const QgsProject *project)
Perform the transaction.
QgsFeatureList featuresFromGML(QDomNodeList featureNodeList, QgsVectorLayer *layer)
Transform GML feature nodes to features.
void writeTransaction(QgsServerInterface *serverIface, const QgsProject *project, const QString &version, const QgsServerRequest &request, QgsServerResponse &response)
Output WFS transaction response.
QgsFeatureRequest parseFilterElement(const QString &typeName, QDomElement &filterElem, QgsProject *project)
Transform a Filter element to a feature request.
QDomDocument createTransactionDocument(QgsServerInterface *serverIface, const QgsProject *project, const QString &version, const QgsServerRequest &request)
Create a wfs transaction document.
transactionInsert parseInsertActionElement(QDomElement &actionElem)
Transform Insert element to transactionInsert.
transactionDelete parseDeleteActionElement(QDomElement &actionElem, const QgsProject *project)
Transform Delete element to transactionDelete.
transactionUpdate parseUpdateActionElement(QDomElement &actionElem, const QgsProject *project)
Transform Update element to transactionUpdate.
QList< QgsFeature > QgsFeatureList
Definition: qgsfeature.h:882
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeatureid.h:37
QList< int > QgsAttributeList
Definition: qgsfield.h:26
const QgsField & field
Definition: qgsfield.h:463
const QString & typeName
The Context struct stores the current layer and coordinate transform context.
Definition: qgsogcutils.h:60
QgsFeatureRequest featureRequest
QList< transactionInsert > inserts
QList< transactionDelete > deletes
QList< transactionUpdate > updates
QgsFeatureRequest featureRequest
QMap< QString, QString > propertyMap