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