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