QGIS API Documentation  3.8.0-Zanzibar (11aff65)
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 #ifndef HAVE_SERVER_PYTHON_PLUGINS
218  ( void )serverIface;
219 #endif
220  // store typeName
221  QStringList typeNameList;
222 
223  QList<transactionInsert>::iterator tiIt = aRequest.inserts.begin();
224  for ( ; tiIt != aRequest.inserts.end(); ++tiIt )
225  {
226  QString name = ( *tiIt ).typeName;
227  if ( !typeNameList.contains( name ) )
228  typeNameList << name;
229  }
230  QList<transactionUpdate>::iterator tuIt = aRequest.updates.begin();
231  for ( ; tuIt != aRequest.updates.end(); ++tuIt )
232  {
233  QString name = ( *tuIt ).typeName;
234  if ( !typeNameList.contains( name ) )
235  typeNameList << name;
236  }
237  QList<transactionDelete>::iterator tdIt = aRequest.deletes.begin();
238  for ( ; tdIt != aRequest.deletes.end(); ++tdIt )
239  {
240  QString name = ( *tdIt ).typeName;
241  if ( !typeNameList.contains( name ) )
242  typeNameList << name;
243  }
244 
245 #ifdef HAVE_SERVER_PYTHON_PLUGINS
246  // get access controls
247  QgsAccessControl *accessControl = serverIface->accessControls();
248 #endif
249 
250  //scoped pointer to restore all original layer filters (subsetStrings) when pointer goes out of scope
251  //there's LOTS of potential exit paths here, so we avoid having to restore the filters manually
252  std::unique_ptr< QgsOWSServerFilterRestorer > filterRestorer( new QgsOWSServerFilterRestorer() );
253 
254  // get layers
255  QStringList wfsLayerIds = QgsServerProjectUtils::wfsLayerIds( *project );
259  QMap<QString, QgsVectorLayer *> mapLayerMap;
260  for ( int i = 0; i < wfsLayerIds.size(); ++i )
261  {
262  QgsMapLayer *layer = project->mapLayer( wfsLayerIds.at( i ) );
263  if ( !layer )
264  {
265  continue;
266  }
267  if ( layer->type() != QgsMapLayerType::VectorLayer )
268  {
269  continue;
270  }
271 
272  QString name = layerTypeName( layer );
273 
274  if ( !typeNameList.contains( name ) )
275  {
276  continue;
277  }
278 
279  // get vector layer
280  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
281  if ( !vlayer )
282  {
283  throw QgsRequestNotWellFormedException( QStringLiteral( "Layer error on '%1'" ).arg( name ) );
284  }
285 
286  //get provider
287  QgsVectorDataProvider *provider = vlayer->dataProvider();
288  if ( !provider )
289  {
290  throw QgsRequestNotWellFormedException( QStringLiteral( "Provider error on layer '%1'" ).arg( name ) );
291  }
292 
293  // get provider capabilities
294  int cap = provider->capabilities();
297  {
298  throw QgsRequestNotWellFormedException( QStringLiteral( "No capabilities to do WFS changes on layer '%1'" ).arg( name ) );
299  }
300 
301  if ( !wfstUpdateLayerIds.contains( vlayer->id() )
302  && !wfstDeleteLayerIds.contains( vlayer->id() )
303  && !wfstInsertLayerIds.contains( vlayer->id() ) )
304  {
305  throw QgsSecurityAccessException( QStringLiteral( "No permissions to do WFS changes on layer '%1'" ).arg( name ) );
306  }
307 #ifdef HAVE_SERVER_PYTHON_PLUGINS
308  if ( accessControl && !accessControl->layerUpdatePermission( vlayer )
309  && !accessControl->layerDeletePermission( vlayer ) && !accessControl->layerInsertPermission( vlayer ) )
310  {
311  throw QgsSecurityAccessException( QStringLiteral( "No permissions to do WFS changes on layer '%1'" ).arg( name ) );
312  }
313 
314  if ( accessControl )
315  {
316  QgsOWSServerFilterRestorer::applyAccessControlLayerFilters( accessControl, vlayer, filterRestorer->originalFilters() );
317  }
318 #endif
319  // store layers
320  mapLayerMap[name] = vlayer;
321  }
322 
323  // perform updates
324  tuIt = aRequest.updates.begin();
325  for ( ; tuIt != aRequest.updates.end(); ++tuIt )
326  {
327  transactionUpdate &action = *tuIt;
328  QString typeName = action.typeName;
329 
330  if ( !mapLayerMap.keys().contains( typeName ) )
331  {
332  action.error = true;
333  action.errorMsg = QStringLiteral( "TypeName '%1' unknown" ).arg( typeName );
334  continue;
335  }
336 
337  // get vector layer
338  QgsVectorLayer *vlayer = mapLayerMap[typeName];
339 
340  // verifying specific permissions
341  if ( !wfstUpdateLayerIds.contains( vlayer->id() ) )
342  {
343  action.error = true;
344  action.errorMsg = QStringLiteral( "No permissions to do WFS updates on layer '%1'" ).arg( typeName );
345  continue;
346  }
347 #ifdef HAVE_SERVER_PYTHON_PLUGINS
348  if ( accessControl && !accessControl->layerUpdatePermission( vlayer ) )
349  {
350  action.error = true;
351  action.errorMsg = QStringLiteral( "No permissions to do WFS updates on layer '%1'" ).arg( typeName );
352  continue;
353  }
354 #endif
355  //get provider
356  QgsVectorDataProvider *provider = vlayer->dataProvider();
357 
358  // verifying specific capabilities
359  int cap = provider->capabilities();
361  {
362  action.error = true;
363  action.errorMsg = QStringLiteral( "No capabilities to do WFS updates on layer '%1'" ).arg( typeName );
364  continue;
365  }
366  // start editing
367  vlayer->startEditing();
368 
369  // update request
370  QgsFeatureRequest featureRequest = action.featureRequest;
371 
372  // expression context
373  QgsExpressionContext expressionContext;
374  expressionContext << QgsExpressionContextUtils::globalScope()
377  featureRequest.setExpressionContext( expressionContext );
378 #ifdef HAVE_SERVER_PYTHON_PLUGINS
379  if ( accessControl )
380  {
381  accessControl->filterFeatures( vlayer, featureRequest );
382  }
383 #endif
384  // get iterator
385  QgsFeatureIterator fit = vlayer->getFeatures( featureRequest );
386  QgsFeature feature;
387  int totalUpdated = 0;
388  // get action properties
389  QMap<QString, QString> propertyMap = action.propertyMap;
390  QDomElement geometryElem = action.geometryElement;
391  // get field information
392  QgsFields fields = provider->fields();
393  const QMap<QString, int> fieldMap = provider->fieldNameMap();
394  QMap<QString, int>::const_iterator fieldMapIt;
395  QString fieldName;
396  bool conversionSuccess;
397  // Update the features
398  while ( fit.nextFeature( feature ) )
399  {
400 #ifdef HAVE_SERVER_PYTHON_PLUGINS
401  if ( accessControl && !accessControl->allowToEdit( vlayer, feature ) )
402  {
403  action.error = true;
404  action.errorMsg = QStringLiteral( "Feature modify permission denied on layer '%1'" ).arg( typeName );
405  vlayer->rollBack();
406  break;
407  }
408 #endif
409  QMap< QString, QString >::const_iterator it = propertyMap.constBegin();
410  for ( ; it != propertyMap.constEnd(); ++it )
411  {
412  fieldName = it.key();
413  fieldMapIt = fieldMap.find( fieldName );
414  if ( fieldMapIt == fieldMap.constEnd() )
415  {
416  continue;
417  }
418  QgsField field = fields.at( fieldMapIt.value() );
419  QVariant value = it.value();
420  if ( value.isNull() )
421  {
422  if ( field.constraints().constraints() & QgsFieldConstraints::Constraint::ConstraintNotNull )
423  {
424  action.error = true;
425  action.errorMsg = QStringLiteral( "NOT NULL constraint error on layer '%1', field '%2'" ).arg( typeName, field.name() );
426  vlayer->rollBack();
427  break;
428  }
429  }
430  else // Not NULL
431  {
432  if ( field.type() == QVariant::Type::Int )
433  {
434  value = it.value().toInt( &conversionSuccess );
435  if ( !conversionSuccess )
436  {
437  action.error = true;
438  action.errorMsg = QStringLiteral( "Property conversion error on layer '%1'" ).arg( typeName );
439  vlayer->rollBack();
440  break;
441  }
442  }
443  else if ( field.type() == QVariant::Type::Double )
444  {
445  value = it.value().toDouble( &conversionSuccess );
446  if ( !conversionSuccess )
447  {
448  action.error = true;
449  action.errorMsg = QStringLiteral( "Property conversion error on layer '%1'" ).arg( typeName );
450  vlayer->rollBack();
451  break;
452  }
453  }
454  else if ( field.type() == QVariant::Type::LongLong )
455  {
456  value = it.value().toLongLong( &conversionSuccess );
457  if ( !conversionSuccess )
458  {
459  action.error = true;
460  action.errorMsg = QStringLiteral( "Property conversion error on layer '%1'" ).arg( typeName );
461  vlayer->rollBack();
462  break;
463  }
464  }
465  }
466  vlayer->changeAttributeValue( feature.id(), fieldMapIt.value(), value );
467  }
468  if ( action.error )
469  {
470  break;
471  }
472 
473  if ( !geometryElem.isNull() )
474  {
475  QgsGeometry g = QgsOgcUtils::geometryFromGML( geometryElem );
476  if ( g.isNull() )
477  {
478  action.error = true;
479  action.errorMsg = QStringLiteral( "Geometry from GML error on layer '%1'" ).arg( typeName );
480  vlayer->rollBack();
481  break;
482  }
483  if ( !vlayer->changeGeometry( feature.id(), g ) )
484  {
485  action.error = true;
486  action.errorMsg = QStringLiteral( "Error in change geometry on layer '%1'" ).arg( typeName );
487  vlayer->rollBack();
488  break;
489  }
490  }
491  totalUpdated += 1;
492  }
493  if ( action.error )
494  {
495  continue;
496  }
497 #ifdef HAVE_SERVER_PYTHON_PLUGINS
498  // verifying changes
499  if ( accessControl )
500  {
501  fit = vlayer->getFeatures( featureRequest );
502  while ( fit.nextFeature( feature ) )
503  {
504  if ( accessControl && !accessControl->allowToEdit( vlayer, feature ) )
505  {
506  action.error = true;
507  action.errorMsg = QStringLiteral( "Feature modify permission denied on layer '%1'" ).arg( typeName );
508  vlayer->rollBack();
509  break;
510  }
511  }
512  }
513 #endif
514  if ( action.error )
515  {
516  continue;
517  }
518 
519  // Commit the changes of the update elements
520  if ( !vlayer->commitChanges() )
521  {
522  action.error = true;
523  action.errorMsg = QStringLiteral( "Error committing updates: %1" ).arg( vlayer->commitErrors().join( QStringLiteral( "; " ) ) );
524  vlayer->rollBack();
525  continue;
526  }
527  // all the changes are OK!
528  action.totalUpdated = totalUpdated;
529  action.error = false;
530 
531  }
532 
533  // perform deletes
534  tdIt = aRequest.deletes.begin();
535  for ( ; tdIt != aRequest.deletes.end(); ++tdIt )
536  {
537  transactionDelete &action = *tdIt;
538  QString typeName = action.typeName;
539 
540  if ( !mapLayerMap.keys().contains( typeName ) )
541  {
542  action.error = true;
543  action.errorMsg = QStringLiteral( "TypeName '%1' unknown" ).arg( typeName );
544  continue;
545  }
546 
547  // get vector layer
548  QgsVectorLayer *vlayer = mapLayerMap[typeName];
549 
550  // verifying specific permissions
551  if ( !wfstDeleteLayerIds.contains( vlayer->id() ) )
552  {
553  action.error = true;
554  action.errorMsg = QStringLiteral( "No permissions to do WFS deletes on layer '%1'" ).arg( typeName );
555  continue;
556  }
557 #ifdef HAVE_SERVER_PYTHON_PLUGINS
558  if ( accessControl && !accessControl->layerDeletePermission( vlayer ) )
559  {
560  action.error = true;
561  action.errorMsg = QStringLiteral( "No permissions to do WFS deletes on layer '%1'" ).arg( typeName );
562  continue;
563  }
564 #endif
565  //get provider
566  QgsVectorDataProvider *provider = vlayer->dataProvider();
567 
568  // verifying specific capabilities
569  int cap = provider->capabilities();
570  if ( !( cap & QgsVectorDataProvider::DeleteFeatures ) )
571  {
572  action.error = true;
573  action.errorMsg = QStringLiteral( "No capabilities to do WFS deletes on layer '%1'" ).arg( typeName );
574  continue;
575  }
576  // start editing
577  vlayer->startEditing();
578 
579  // update request
580  QgsFeatureRequest featureRequest = action.featureRequest;
581 
582  // expression context
583  QgsExpressionContext expressionContext;
584  expressionContext << QgsExpressionContextUtils::globalScope()
587  featureRequest.setExpressionContext( expressionContext );
588 
589  // get iterator
590  QgsFeatureIterator fit = vlayer->getFeatures( featureRequest );
591  QgsFeature feature;
592  // get deleted fids
593  QgsFeatureIds fids;
594  while ( fit.nextFeature( feature ) )
595  {
596 #ifdef HAVE_SERVER_PYTHON_PLUGINS
597  if ( accessControl && !accessControl->allowToEdit( vlayer, feature ) )
598  {
599  action.error = true;
600  action.errorMsg = QStringLiteral( "Feature modify permission denied" );
601  vlayer->rollBack();
602  break;
603  }
604 #endif
605  fids << feature.id();
606  }
607  if ( action.error )
608  {
609  continue;
610  }
611  // delete features
612  if ( !vlayer->deleteFeatures( fids ) )
613  {
614  action.error = true;
615  action.errorMsg = QStringLiteral( "Delete features failed on layer '%1'" ).arg( typeName );
616  vlayer->rollBack();
617  continue;
618  }
619 
620  // Commit the changes of the update elements
621  if ( !vlayer->commitChanges() )
622  {
623  action.error = true;
624  action.errorMsg = QStringLiteral( "Error committing deletes: %1" ).arg( vlayer->commitErrors().join( QStringLiteral( "; " ) ) );
625  vlayer->rollBack();
626  continue;
627  }
628  // all the changes are OK!
629  action.totalDeleted = fids.count();
630  action.error = false;
631  }
632 
633  // perform inserts
634  tiIt = aRequest.inserts.begin();
635  for ( ; tiIt != aRequest.inserts.end(); ++tiIt )
636  {
637  transactionInsert &action = *tiIt;
638  QString typeName = action.typeName;
639 
640  if ( !mapLayerMap.keys().contains( typeName ) )
641  {
642  action.error = true;
643  action.errorMsg = QStringLiteral( "TypeName '%1' unknown" ).arg( typeName );
644  continue;
645  }
646 
647  // get vector layer
648  QgsVectorLayer *vlayer = mapLayerMap[typeName];
649 
650  // verifying specific permissions
651  if ( !wfstInsertLayerIds.contains( vlayer->id() ) )
652  {
653  action.error = true;
654  action.errorMsg = QStringLiteral( "No permissions to do WFS inserts on layer '%1'" ).arg( typeName );
655  continue;
656  }
657 #ifdef HAVE_SERVER_PYTHON_PLUGINS
658  if ( accessControl && !accessControl->layerDeletePermission( vlayer ) )
659  {
660  action.error = true;
661  action.errorMsg = QStringLiteral( "No permissions to do WFS inserts on layer '%1'" ).arg( typeName );
662  continue;
663  }
664 #endif
665  //get provider
666  QgsVectorDataProvider *provider = vlayer->dataProvider();
667 
668  // verifying specific capabilities
669  int cap = provider->capabilities();
670  if ( !( cap & QgsVectorDataProvider::AddFeatures ) )
671  {
672  action.error = true;
673  action.errorMsg = QStringLiteral( "No capabilities to do WFS inserts on layer '%1'" ).arg( typeName );
674  continue;
675  }
676 
677  // start editing
678  vlayer->startEditing();
679 
680  // get inserting features
681  QgsFeatureList featureList;
682  try
683  {
684  featureList = featuresFromGML( action.featureNodeList, provider );
685  }
686  catch ( QgsOgcServiceException &ex )
687  {
688  action.error = true;
689  action.errorMsg = QStringLiteral( "%1 '%2'" ).arg( ex.message() ).arg( typeName );
690  continue;
691  }
692 #ifdef HAVE_SERVER_PYTHON_PLUGINS
693  // control features
694  if ( accessControl )
695  {
696  QgsFeatureList::iterator featureIt = featureList.begin();
697  while ( featureIt != featureList.end() )
698  {
699  if ( !accessControl->allowToEdit( vlayer, *featureIt ) )
700  {
701  action.error = true;
702  action.errorMsg = QStringLiteral( "Feature modify permission denied on layer '%1'" ).arg( typeName );
703  vlayer->rollBack();
704  break;
705  }
706  featureIt++;
707  }
708  }
709 #endif
710  if ( action.error )
711  {
712  continue;
713  }
714 
715  // perform add features
716  if ( !provider->addFeatures( featureList ) )
717  {
718  action.error = true;
719  action.errorMsg = QStringLiteral( "Insert features failed on layer '%1'" ).arg( typeName );
720  if ( provider ->hasErrors() )
721  {
722  provider->clearErrors();
723  }
724  vlayer->rollBack();
725  continue;
726  }
727 
728  // Commit the changes of the update elements
729  if ( !vlayer->commitChanges() )
730  {
731  action.error = true;
732  action.errorMsg = QStringLiteral( "Error committing inserts: %1" ).arg( vlayer->commitErrors().join( QStringLiteral( "; " ) ) );
733  vlayer->rollBack();
734  continue;
735  }
736  // all changes are OK!
737  action.error = false;
738 
739  // Get the Feature Ids of the inserted feature
740  for ( int j = 0; j < featureList.size(); j++ )
741  {
742  action.insertFeatureIds << typeName + "." + QString::number( featureList[j].id() );
743  }
744  }
745 
746  //force restoration of original layer filters
747  filterRestorer.reset();
748  }
749 
750  QgsFeatureList featuresFromGML( QDomNodeList featureNodeList, QgsVectorDataProvider *provider )
751  {
752  // Store the inserted features
753  QgsFeatureList featList;
754 
755  // Get Layer Field Information
756  QgsFields fields = provider->fields();
757  const QMap<QString, int> fieldMap = provider->fieldNameMap();
758  QMap<QString, int>::const_iterator fieldMapIt;
759 
760  for ( int i = 0; i < featureNodeList.count(); i++ )
761  {
762  QgsFeature feat( fields );
763 
764  QDomElement featureElem = featureNodeList.at( i ).toElement();
765  QDomNode currentAttributeChild = featureElem.firstChild();
766  bool conversionSuccess = true;
767 
768  while ( !currentAttributeChild.isNull() )
769  {
770  QDomElement currentAttributeElement = currentAttributeChild.toElement();
771  QString attrName = currentAttributeElement.localName();
772 
773  if ( attrName != QLatin1String( "boundedBy" ) )
774  {
775  if ( attrName != QLatin1String( "geometry" ) ) //a normal attribute
776  {
777  fieldMapIt = fieldMap.find( attrName );
778  if ( fieldMapIt == fieldMap.constEnd() )
779  {
780  continue;
781  }
782 
783  QgsField field = fields.at( fieldMapIt.value() );
784  QString attrValue = currentAttributeElement.text();
785  int attrType = field.type();
786 
787  QgsMessageLog::logMessage( QStringLiteral( "attr: name=%1 idx=%2 value=%3" ).arg( attrName ).arg( fieldMapIt.value() ).arg( attrValue ) );
788 
789  if ( attrType == QVariant::Int )
790  feat.setAttribute( fieldMapIt.value(), attrValue.toInt( &conversionSuccess ) );
791  else if ( attrType == QVariant::Double )
792  feat.setAttribute( fieldMapIt.value(), attrValue.toDouble( &conversionSuccess ) );
793  else
794  feat.setAttribute( fieldMapIt.value(), attrValue );
795 
796  if ( !conversionSuccess )
797  {
798  throw QgsRequestNotWellFormedException( QStringLiteral( "Property conversion error on layer insert" ) );
799  }
800  }
801  else //a geometry attribute
802  {
803  QgsGeometry g = QgsOgcUtils::geometryFromGML( currentAttributeElement );
804  if ( g.isNull() )
805  {
806  throw QgsRequestNotWellFormedException( QStringLiteral( "Geometry from GML error on layer insert" ) );
807  }
808  feat.setGeometry( g );
809  }
810  }
811  currentAttributeChild = currentAttributeChild.nextSibling();
812  }
813  // update feature list
814  featList << feat;
815  }
816  return featList;
817  }
818 
820  {
821  if ( !parameters.contains( QStringLiteral( "OPERATION" ) ) )
822  {
823  throw QgsRequestNotWellFormedException( QStringLiteral( "OPERATION parameter is mandatory" ) );
824  }
825  if ( parameters.value( QStringLiteral( "OPERATION" ) ).toUpper() != QStringLiteral( "DELETE" ) )
826  {
827  throw QgsRequestNotWellFormedException( QStringLiteral( "Only DELETE value is defined for OPERATION parameter" ) );
828  }
829 
830  // Verifying parameters mutually exclusive
831  if ( ( parameters.contains( QStringLiteral( "FEATUREID" ) )
832  && ( parameters.contains( QStringLiteral( "FILTER" ) ) || parameters.contains( QStringLiteral( "BBOX" ) ) ) )
833  || ( parameters.contains( QStringLiteral( "FILTER" ) )
834  && ( parameters.contains( QStringLiteral( "FEATUREID" ) ) || parameters.contains( QStringLiteral( "BBOX" ) ) ) )
835  || ( parameters.contains( QStringLiteral( "BBOX" ) )
836  && ( parameters.contains( QStringLiteral( "FEATUREID" ) ) || parameters.contains( QStringLiteral( "FILTER" ) ) ) )
837  )
838  {
839  throw QgsRequestNotWellFormedException( QStringLiteral( "FEATUREID FILTER and BBOX parameters are mutually exclusive" ) );
840  }
841 
842  transactionRequest request;
843 
844  QStringList typeNameList;
845  // parse FEATUREID
846  if ( parameters.contains( QStringLiteral( "FEATUREID" ) ) )
847  {
848  QStringList fidList = parameters.value( QStringLiteral( "FEATUREID" ) ).split( ',' );
849 
850  QMap<QString, QgsFeatureIds> fidsMap;
851 
852  QStringList::const_iterator fidIt = fidList.constBegin();
853  for ( ; fidIt != fidList.constEnd(); ++fidIt )
854  {
855  // Get FeatureID
856  QString fid = *fidIt;
857  fid = fid.trimmed();
858  // testing typename in the WFS featureID
859  if ( !fid.contains( '.' ) )
860  {
861  throw QgsRequestNotWellFormedException( QStringLiteral( "FEATUREID has to have TYPENAME in the values" ) );
862  }
863 
864  QString typeName = fid.section( '.', 0, 0 );
865  fid = fid.section( '.', 1, 1 );
866  if ( !typeNameList.contains( typeName ) )
867  {
868  typeNameList << typeName;
869  }
870 
871  QgsFeatureIds fids;
872  if ( fidsMap.contains( typeName ) )
873  {
874  fids = fidsMap.value( typeName );
875  }
876  fids.insert( fid.toInt() );
877  fidsMap.insert( typeName, fids );
878  }
879 
880  QMap<QString, QgsFeatureIds>::const_iterator fidsMapIt = fidsMap.constBegin();
881  while ( fidsMapIt != fidsMap.constEnd() )
882  {
883  transactionDelete action;
884  action.typeName = fidsMapIt.key();
885 
886  QgsFeatureIds fids = fidsMapIt.value();
887  action.featureRequest = QgsFeatureRequest( fids );
888 
889  request.deletes.append( action );
890  }
891  return request;
892  }
893 
894  if ( !parameters.contains( QStringLiteral( "TYPENAME" ) ) )
895  {
896  throw QgsRequestNotWellFormedException( QStringLiteral( "TYPENAME is mandatory except if FEATUREID is used" ) );
897  }
898 
899  typeNameList = parameters.value( QStringLiteral( "TYPENAME" ) ).split( ',' );
900 
901  // Create actions based on TypeName
902  QStringList::const_iterator typeNameIt = typeNameList.constBegin();
903  for ( ; typeNameIt != typeNameList.constEnd(); ++typeNameIt )
904  {
905  QString typeName = *typeNameIt;
906  typeName = typeName.trimmed();
907 
908  transactionDelete action;
909  action.typeName = typeName;
910 
911  request.deletes.append( action );
912  }
913 
914  // Manage extra parameter exp_filter
915  if ( parameters.contains( QStringLiteral( "EXP_FILTER" ) ) )
916  {
917  QString expFilterName = parameters.value( QStringLiteral( "EXP_FILTER" ) );
918  QStringList expFilterList;
919  QRegExp rx( "\\(([^()]+)\\)" );
920  if ( rx.indexIn( expFilterName, 0 ) == -1 )
921  {
922  expFilterList << expFilterName;
923  }
924  else
925  {
926  int pos = 0;
927  while ( ( pos = rx.indexIn( expFilterName, pos ) ) != -1 )
928  {
929  expFilterList << rx.cap( 1 );
930  pos += rx.matchedLength();
931  }
932  }
933 
934  // Verifying the 1:1 mapping between TYPENAME and EXP_FILTER but without exception
935  if ( request.deletes.size() == expFilterList.size() )
936  {
937  // set feature request filter expression based on filter element
938  QList<transactionDelete>::iterator dIt = request.deletes.begin();
939  QStringList::const_iterator expFilterIt = expFilterList.constBegin();
940  for ( ; dIt != request.deletes.end(); ++dIt )
941  {
942  transactionDelete &action = *dIt;
943  // Get Filter for this typeName
944  QString expFilter;
945  if ( expFilterIt != expFilterList.constEnd() )
946  {
947  expFilter = *expFilterIt;
948  }
949  std::shared_ptr<QgsExpression> filter( new QgsExpression( expFilter ) );
950  if ( filter )
951  {
952  if ( filter->hasParserError() )
953  {
954  QgsMessageLog::logMessage( filter->parserErrorString() );
955  }
956  else
957  {
958  if ( filter->needsGeometry() )
959  {
961  }
962  action.featureRequest.setFilterExpression( filter->expression() );
963  }
964  }
965  }
966  }
967  else
968  {
969  QgsMessageLog::logMessage( "There has to be a 1:1 mapping between each element in a TYPENAME and the EXP_FILTER list" );
970  }
971  }
972 
973  if ( parameters.contains( QStringLiteral( "BBOX" ) ) )
974  {
975  // get bbox value
976  QString bbox = parameters.value( QStringLiteral( "BBOX" ) );
977  if ( bbox.isEmpty() )
978  {
979  throw QgsRequestNotWellFormedException( QStringLiteral( "BBOX parameter is empty" ) );
980  }
981 
982  // get bbox corners
983  QStringList corners = bbox.split( ',' );
984  if ( corners.size() != 4 )
985  {
986  throw QgsRequestNotWellFormedException( QStringLiteral( "BBOX has to be composed of 4 elements: '%1'" ).arg( bbox ) );
987  }
988 
989  // convert corners to double
990  double d[4];
991  bool ok;
992  for ( int i = 0; i < 4; i++ )
993  {
994  corners[i].replace( ' ', '+' );
995  d[i] = corners[i].toDouble( &ok );
996  if ( !ok )
997  {
998  throw QgsRequestNotWellFormedException( QStringLiteral( "BBOX has to be composed of 4 double: '%1'" ).arg( bbox ) );
999  }
1000  }
1001  // create extent
1002  QgsRectangle extent( d[0], d[1], d[2], d[3] );
1003 
1004  // set feature request filter rectangle
1005  QList<transactionDelete>::iterator dIt = request.deletes.begin();
1006  for ( ; dIt != request.deletes.end(); ++dIt )
1007  {
1008  transactionDelete &action = *dIt;
1009  action.featureRequest.setFilterRect( extent );
1010  }
1011  return request;
1012  }
1013  else if ( parameters.contains( QStringLiteral( "FILTER" ) ) )
1014  {
1015  QString filterName = parameters.value( QStringLiteral( "FILTER" ) );
1016  QStringList filterList;
1017  QRegExp rx( "\\(([^()]+)\\)" );
1018  if ( rx.indexIn( filterName, 0 ) == -1 )
1019  {
1020  filterList << filterName;
1021  }
1022  else
1023  {
1024  int pos = 0;
1025  while ( ( pos = rx.indexIn( filterName, pos ) ) != -1 )
1026  {
1027  filterList << rx.cap( 1 );
1028  pos += rx.matchedLength();
1029  }
1030  }
1031 
1032  // Verifying the 1:1 mapping between TYPENAME and FILTER
1033  if ( request.deletes.size() != filterList.size() )
1034  {
1035  throw QgsRequestNotWellFormedException( QStringLiteral( "There has to be a 1:1 mapping between each element in a TYPENAME and the FILTER list" ) );
1036  }
1037 
1038  // set feature request filter expression based on filter element
1039  QList<transactionDelete>::iterator dIt = request.deletes.begin();
1040  QStringList::const_iterator filterIt = filterList.constBegin();
1041  for ( ; dIt != request.deletes.end(); ++dIt )
1042  {
1043  transactionDelete &action = *dIt;
1044 
1045  // Get Filter for this typeName
1046  QDomDocument filter;
1047  if ( filterIt != filterList.constEnd() )
1048  {
1049  QString errorMsg;
1050  if ( !filter.setContent( *filterIt, true, &errorMsg ) )
1051  {
1052  throw QgsRequestNotWellFormedException( QStringLiteral( "error message: %1. The XML string was: %2" ).arg( errorMsg, *filterIt ) );
1053  }
1054  }
1055 
1056  QDomElement filterElem = filter.firstChildElement();
1057  action.featureRequest = parseFilterElement( action.typeName, filterElem );
1058 
1059  if ( filterIt != filterList.constEnd() )
1060  {
1061  ++filterIt;
1062  }
1063  }
1064  return request;
1065  }
1066 
1067  return request;
1068  }
1069 
1071  {
1072  transactionRequest request;
1073 
1074  QDomNodeList docChildNodes = docElem.childNodes();
1075 
1076  QDomElement actionElem;
1077  QString actionName;
1078 
1079  for ( int i = docChildNodes.count(); 0 < i; --i )
1080  {
1081  actionElem = docChildNodes.at( i - 1 ).toElement();
1082  actionName = actionElem.localName();
1083 
1084  if ( actionName == QLatin1String( "Insert" ) )
1085  {
1086  transactionInsert action = parseInsertActionElement( actionElem );
1087  request.inserts.append( action );
1088  }
1089  else if ( actionName == QLatin1String( "Update" ) )
1090  {
1091  transactionUpdate action = parseUpdateActionElement( actionElem );
1092  request.updates.append( action );
1093  }
1094  else if ( actionName == QLatin1String( "Delete" ) )
1095  {
1096  transactionDelete action = parseDeleteActionElement( actionElem );
1097  request.deletes.append( action );
1098  }
1099  }
1100 
1101  return request;
1102  }
1103 
1104  transactionDelete parseDeleteActionElement( QDomElement &actionElem )
1105  {
1106  QString typeName = actionElem.attribute( QStringLiteral( "typeName" ) );
1107  if ( typeName.contains( ':' ) )
1108  typeName = typeName.section( ':', 1, 1 );
1109 
1110  QDomElement filterElem = actionElem.firstChild().toElement();
1111  if ( filterElem.tagName() != QLatin1String( "Filter" ) )
1112  {
1113  throw QgsRequestNotWellFormedException( QStringLiteral( "Delete action element first child is not Filter" ) );
1114  }
1115 
1116  QgsFeatureRequest featureRequest = parseFilterElement( typeName, filterElem );
1117 
1118  transactionDelete action;
1119  action.typeName = typeName;
1120  action.featureRequest = featureRequest;
1121  action.error = false;
1122 
1123  if ( actionElem.hasAttribute( QStringLiteral( "handle" ) ) )
1124  {
1125  action.handle = actionElem.attribute( QStringLiteral( "handle" ) );
1126  }
1127 
1128  return action;
1129  }
1130 
1131  transactionUpdate parseUpdateActionElement( QDomElement &actionElem )
1132  {
1133  QString typeName = actionElem.attribute( QStringLiteral( "typeName" ) );
1134  if ( typeName.contains( ':' ) )
1135  typeName = typeName.section( ':', 1, 1 );
1136 
1137  QDomNodeList propertyNodeList = actionElem.elementsByTagName( QStringLiteral( "Property" ) );
1138  if ( propertyNodeList.isEmpty() )
1139  {
1140  throw QgsRequestNotWellFormedException( QStringLiteral( "Update action element must have one or more Property element" ) );
1141  }
1142 
1143  QMap<QString, QString> propertyMap;
1144  QDomElement propertyElem;
1145  QDomElement nameElem;
1146  QDomElement valueElem;
1147  QDomElement geometryElem;
1148 
1149  for ( int l = 0; l < propertyNodeList.count(); ++l )
1150  {
1151  propertyElem = propertyNodeList.at( l ).toElement();
1152  nameElem = propertyElem.elementsByTagName( QStringLiteral( "Name" ) ).at( 0 ).toElement();
1153  valueElem = propertyElem.elementsByTagName( QStringLiteral( "Value" ) ).at( 0 ).toElement();
1154  if ( nameElem.text() != QLatin1String( "geometry" ) )
1155  {
1156  propertyMap.insert( nameElem.text(), valueElem.text() );
1157  }
1158  else
1159  {
1160  geometryElem = valueElem;
1161  }
1162  }
1163 
1164  QDomNodeList filterNodeList = actionElem.elementsByTagName( QStringLiteral( "Filter" ) );
1165  QgsFeatureRequest featureRequest;
1166  if ( filterNodeList.size() != 0 )
1167  {
1168  QDomElement filterElem = filterNodeList.at( 0 ).toElement();
1169  featureRequest = parseFilterElement( typeName, filterElem );
1170  }
1171 
1172  transactionUpdate action;
1173  action.typeName = typeName;
1174  action.propertyMap = propertyMap;
1175  action.geometryElement = geometryElem;
1176  action.featureRequest = featureRequest;
1177  action.error = false;
1178 
1179  if ( actionElem.hasAttribute( QStringLiteral( "handle" ) ) )
1180  {
1181  action.handle = actionElem.attribute( QStringLiteral( "handle" ) );
1182  }
1183 
1184  return action;
1185  }
1186 
1187  transactionInsert parseInsertActionElement( QDomElement &actionElem )
1188  {
1189  QDomNodeList featureNodeList = actionElem.childNodes();
1190  if ( featureNodeList.size() != 1 )
1191  {
1192  throw QgsRequestNotWellFormedException( QStringLiteral( "Insert action element must have one or more child node" ) );
1193  }
1194 
1195  QString typeName;
1196  for ( int i = 0; i < featureNodeList.count(); ++i )
1197  {
1198  QString tempTypeName = featureNodeList.at( i ).toElement().localName();
1199  if ( tempTypeName.contains( ':' ) )
1200  tempTypeName = tempTypeName.section( ':', 1, 1 );
1201 
1202  if ( typeName.isEmpty() )
1203  {
1204  typeName = tempTypeName;
1205  }
1206  else if ( tempTypeName != typeName )
1207  {
1208  throw QgsRequestNotWellFormedException( QStringLiteral( "Insert action element must have one typename features" ) );
1209  }
1210  }
1211 
1212  transactionInsert action;
1213  action.typeName = typeName;
1214  action.featureNodeList = featureNodeList;
1215  action.error = false;
1216 
1217  if ( actionElem.hasAttribute( QStringLiteral( "handle" ) ) )
1218  {
1219  action.handle = actionElem.attribute( QStringLiteral( "handle" ) );
1220  }
1221 
1222  return action;
1223  }
1224 
1225  namespace
1226  {
1227 
1228  void addTransactionResult( QDomDocument &responseDoc, QDomElement &resultsElem,
1229  const QString &locator, const QString &message )
1230  {
1231  QDomElement trElem = responseDoc.createElement( QStringLiteral( "Action" ) );
1232  resultsElem.appendChild( trElem );
1233 
1234  if ( !locator.isEmpty() )
1235  {
1236  trElem.setAttribute( QStringLiteral( "locator" ), locator );
1237  }
1238 
1239  if ( !message.isEmpty() )
1240  {
1241  QDomElement mesElem = responseDoc.createElement( QStringLiteral( "Message" ) );
1242  mesElem.appendChild( responseDoc.createTextNode( message ) );
1243  trElem.appendChild( mesElem );
1244  }
1245  }
1246 
1247  }
1248 
1249 } // namespace QgsWfs
1250 
1251 
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: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.
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: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...
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
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.
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