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