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