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