QGIS API Documentation  3.14.0-Pi (9f7028fd23)
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( request.data(), 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  if ( action.error )
526  {
527  continue;
528  }
529 #endif
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 
858  transactionRequest parseTransactionParameters( QgsServerRequest::Parameters parameters, const QgsProject *project )
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();
926  action.featureRequest = QgsFeatureRequest();
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  {
999  action.featureRequest.setFlags( QgsFeatureRequest::NoFlags );
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;
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 
QgsVectorLayer::getFeatures
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
Definition: qgsvectorlayer.cpp:993
QgsServerRequest::parameters
QgsServerRequest::Parameters parameters() const
Returns a map of query parameters with keys converted to uppercase.
Definition: qgsserverrequest.cpp:85
QgsExpressionContext
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
Definition: qgsexpressioncontext.h:369
qgsfields.h
qgsexpressioncontextutils.h
qgswfsutils.h
qgsserverfeatureid.h
QgsVectorDataProvider::DeleteFeatures
@ DeleteFeatures
Allows deletion of features.
Definition: qgsvectordataprovider.h:76
QgsWfs::parseTransactionRequestBody
transactionRequest parseTransactionRequestBody(QDomElement &docElem, const QgsProject *project)
Transform RequestBody root element to getFeatureRequest.
Definition: qgswfstransaction.cpp:1129
QgsVectorDataProvider::pkAttributeIndexes
virtual QgsAttributeList pkAttributeIndexes() const
Returns list of indexes of fields that make up the primary key.
Definition: qgsvectordataprovider.cpp:333
QgsVectorLayer::dataProvider
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer's data provider, it may be nullptr.
Definition: qgsvectorlayer.cpp:627
QgsWfs::parseFilterElement
QgsFeatureRequest parseFilterElement(const QString &typeName, QDomElement &filterElem, QgsProject *project)
Transform a Filter element to a feature request.
Definition: qgswfsutils.cpp:121
QgsExpressionContextUtils::globalScope
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context.
Definition: qgsexpressioncontextutils.cpp:33
QgsMapLayerType::VectorLayer
@ VectorLayer
QgsWfs::parseUpdateActionElement
transactionUpdate parseUpdateActionElement(QDomElement &actionElem, const QgsProject *project)
Transform Update element to transactionUpdate.
Definition: qgswfstransaction.cpp:1192
QgsServerFeatureId::getServerFid
SERVER_EXPORT QString getServerFid(const QgsFeature &feature, const QgsAttributeList &pkAttributes)
Returns the feature id based on primary keys.
Definition: qgsserverfeatureid.cpp:24
qgsexpression.h
QgsVectorDataProvider::fields
QgsFields fields() const override=0
Returns the fields associated with this data provider.
QgsWfs::parseInsertActionElement
transactionInsert parseInsertActionElement(QDomElement &actionElem)
Transform Insert element to transactionInsert.
Definition: qgswfstransaction.cpp:1252
QgsWfs::layerTypeName
QString layerTypeName(const QgsMapLayer *layer)
Returns typename from vector layer.
Definition: qgswfsutils.cpp:89
QgsAccessControl::filterFeatures
void filterFeatures(const QgsVectorLayer *layer, QgsFeatureRequest &filterFeatures) const override
Filter the features of the layer.
Definition: qgsaccesscontrol.cpp:62
qgsfeatureiterator.h
QgsFields
Definition: qgsfields.h:44
QgsExpressionContextUtils::layerScope
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
Definition: qgsexpressioncontextutils.cpp:264
qgsserverprojectutils.h
QgsServerRequest
Definition: qgsserverrequest.h:38
QgsVectorLayer::startEditing
Q_INVOKABLE bool startEditing()
Makes the layer editable.
Definition: qgsvectorlayer.cpp:1411
QgsServerProjectUtils::wfstInsertLayerIds
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.
Definition: qgsserverprojectutils.cpp:352
QgsProject::mapLayer
Q_INVOKABLE QgsMapLayer * mapLayer(const QString &layerId) const
Retrieve a pointer to a registered layer by layer ID.
Definition: qgsproject.cpp:3124
QgsAttributeList
QList< int > QgsAttributeList
Definition: qgsfield.h:26
QgsField::name
QString name
Definition: qgsfield.h:59
QgsServerInterface::accessControls
virtual QgsAccessControl * accessControls() const =0
Gets the registered access control filters.
QgsRectangle
Definition: qgsrectangle.h:41
QgsVectorLayer::changeAttributeValue
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).
Definition: qgsvectorlayer.cpp:2965
qgsogcutils.h
QgsVectorDataProvider::ChangeGeometries
@ ChangeGeometries
Allows modifications of geometries.
Definition: qgsvectordataprovider.h:82
QgsProject
Definition: qgsproject.h:92
QgsFeatureRequest::setExpressionContext
QgsFeatureRequest & setExpressionContext(const QgsExpressionContext &context)
Sets the expression context used to evaluate filter expressions.
Definition: qgsfeaturerequest.cpp:149
QgsServerRequest::Parameters
QMap< QString, QString > Parameters
Definition: qgsserverrequest.h:59
QgsWfs::WFS_NAMESPACE
const QString WFS_NAMESPACE
Definition: qgswfsutils.h:91
QgsServerResponse::write
virtual void write(const QString &data)
Write string This is a convenient method that will write directly to the underlying I/O device.
Definition: qgsserverresponse.cpp:25
QgsOWSServerFilterRestorer
RAII class to restore layer filters on destruction.
Definition: qgsfilterrestorer.h:36
qgsfilterrestorer.h
QgsVectorLayer::changeGeometry
bool changeGeometry(QgsFeatureId fid, QgsGeometry &geometry, bool skipDefaultValue=false)
Changes a feature's geometry within the layer's edit buffer (but does not immediately commit the chan...
Definition: qgsvectorlayer.cpp:2941
QgsFeature::id
QgsFeatureId id
Definition: qgsfeature.h:68
QgsFeatureRequest
Definition: qgsfeaturerequest.h:75
QgsVectorDataProvider::capabilities
virtual QgsVectorDataProvider::Capabilities capabilities() const
Returns flags containing the supported capabilities.
Definition: qgsvectordataprovider.cpp:191
QgsVectorDataProvider::fieldNameMap
QMap< QString, int > fieldNameMap() const
Returns a map where the key is the name of the field and the value is its index.
Definition: qgsvectordataprovider.cpp:315
QgsExpressionContextUtils::projectScope
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
Definition: qgsexpressioncontextutils.cpp:221
QgsAccessControl::layerUpdatePermission
bool layerUpdatePermission(const QgsVectorLayer *layer) const
Returns the layer update right.
Definition: qgsaccesscontrol.cpp:133
QgsFeatureRequest::NoFlags
@ NoFlags
Definition: qgsfeaturerequest.h:106
QgsServerProjectUtils::wfsLayerIds
SERVER_EXPORT QStringList wfsLayerIds(const QgsProject &project)
Returns the Layer ids list defined in a QGIS project as published in WFS.
Definition: qgsserverprojectutils.cpp:337
QgsServerProjectUtils::wfstDeleteLayerIds
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.
Definition: qgsserverprojectutils.cpp:357
Qgis::Info
@ Info
Definition: qgis.h:103
QgsVectorLayer::commitErrors
QStringList commitErrors() const
Returns a list containing any error messages generated when attempting to commit changes to the layer...
Definition: qgsvectorlayer.cpp:3374
QgsWfs::writeTransaction
void writeTransaction(QgsServerInterface *serverIface, const QgsProject *project, const QString &version, const QgsServerRequest &request, QgsServerResponse &response)
Output WFS transaction response.
Definition: qgswfstransaction.cpp:71
qgswfstransaction.h
QgsAccessControl::allowToEdit
bool allowToEdit(const QgsVectorLayer *layer, const QgsFeature &feature) const
Are we authorized to modify the following geometry.
Definition: qgsaccesscontrol.cpp:173
QgsVectorLayer::rollBack
Q_INVOKABLE bool rollBack(bool deleteBuffer=true)
Stops a current editing operation and discards any uncommitted edits.
Definition: qgsvectorlayer.cpp:3379
QgsFieldConstraints::constraints
Constraints constraints
Definition: qgsfieldconstraints.h:36
QgsWfs::OGC_NAMESPACE
const QString OGC_NAMESPACE
Definition: qgswfsutils.h:93
qgsmaplayer.h
QgsWfs
WMS implementation.
Definition: qgswfs.cpp:35
qgsvectordataprovider.h
QgsFeatureList
QList< QgsFeature > QgsFeatureList
Definition: qgsfeature.h:572
QgsVectorLayer::commitChanges
Q_INVOKABLE bool commitChanges()
Attempts to commit to the underlying data provider any buffered changes made since the last to call t...
Definition: qgsvectorlayer.cpp:3329
QgsVectorDataProvider::AddFeatures
@ AddFeatures
Allows adding features.
Definition: qgsvectordataprovider.h:75
QgsWfs::parseDeleteActionElement
transactionDelete parseDeleteActionElement(QDomElement &actionElem, const QgsProject *project)
Transform Delete element to transactionDelete.
Definition: qgswfstransaction.cpp:1163
QgsMapLayer::id
QString id() const
Returns the layer's unique ID, which is used to access this layer from QgsProject.
Definition: qgsmaplayer.cpp:148
QgsWfs::parseTransactionParameters
transactionRequest parseTransactionParameters(QgsServerRequest::Parameters parameters, const QgsProject *project)
Definition: qgswfstransaction.cpp:876
QgsGeometry::isNull
bool isNull
Definition: qgsgeometry.h:125
QgsAccessControl::layerInsertPermission
bool layerInsertPermission(const QgsVectorLayer *layer) const
Returns the layer insert right.
Definition: qgsaccesscontrol.cpp:119
typeName
const QString & typeName
Definition: qgswfsgetfeature.cpp:109
QgsOWSServerFilterRestorer::applyAccessControlLayerFilters
static void applyAccessControlLayerFilters(const QgsAccessControl *accessControl, QgsMapLayer *mapLayer, QHash< QgsMapLayer *, QString > &originalLayerFilters)
Apply filter from AccessControl.
QgsWfs::QgsRequestNotWellFormedException
Exception thrown in case of malformed request.
Definition: qgswfsserviceexception.h:103
qgsserverlogger.h
QgsFeatureIds
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeatureid.h:34
QgsWfs::featuresFromGML
QgsFeatureList featuresFromGML(QDomNodeList featureNodeList, QgsVectorDataProvider *provider)
Transform GML feature nodes to features.
Definition: qgswfstransaction.cpp:807
QgsOgcServiceException
Exception base class for service exceptions.
Definition: qgsserverexception.h:82
QgsVectorDataProvider::clearErrors
void clearErrors()
Clear recorded errors.
Definition: qgsvectordataprovider.cpp:697
qgsvectorlayer.h
QgsOgcUtils::geometryFromGML
static QgsGeometry geometryFromGML(const QString &xmlString, const QgsOgcUtils::Context &context=QgsOgcUtils::Context())
Static method that creates geometry from GML.
Definition: qgsogcutils.cpp:167
QgsAccessControl
A helper class that centralizes restrictions given by all the access control filter plugins.
Definition: qgsaccesscontrol.h:36
QgsServerRequest::data
virtual QByteArray data() const
Returns post/put data Check for QByteArray::isNull() to check if data is available.
Definition: qgsserverrequest.cpp:95
QgsVectorDataProvider::addFeatures
bool addFeatures(QgsFeatureList &flist, QgsFeatureSink::Flags flags=QgsFeatureSink::Flags()) override
Adds a list of features to the sink.
Definition: qgsvectordataprovider.cpp:85
qgsgeometry.h
QgsServerFeatureId::updateFeatureRequestFromServerFids
SERVER_EXPORT QgsFeatureRequest updateFeatureRequestFromServerFids(QgsFeatureRequest &featureRequest, const QStringList &serverFids, const QgsVectorDataProvider *provider)
Returns the feature request based on feature ids build with primary keys.
Definition: qgsserverfeatureid.cpp:40
QgsFeatureIterator::nextFeature
bool nextFeature(QgsFeature &f)
Definition: qgsfeatureiterator.h:373
QgsVectorLayer::deleteFeatures
bool deleteFeatures(const QgsFeatureIds &fids, DeleteContext *context=nullptr)
Deletes a set of features from the layer (but does not commit it)
Definition: qgsvectorlayer.cpp:3264
QgsOgcServiceException::message
QString message() const
Returns the exception message.
Definition: qgsserverexception.h:94
QgsGeometry
Definition: qgsgeometry.h:122
QgsVectorDataProvider::ChangeAttributeValues
@ ChangeAttributeValues
Allows modification of attribute values.
Definition: qgsvectordataprovider.h:77
QgsVectorLayer
Definition: qgsvectorlayer.h:385
QgsAccessControl::layerDeletePermission
bool layerDeletePermission(const QgsVectorLayer *layer) const
Returns the layer delete right.
Definition: qgsaccesscontrol.cpp:147
QgsMapLayer
Definition: qgsmaplayer.h:81
QgsWfs::performTransaction
void performTransaction(transactionRequest &aRequest, QgsServerInterface *serverIface, const QgsProject *project)
Perform the transaction.
Definition: qgswfstransaction.cpp:237
QgsMessageLog::logMessage
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).
Definition: qgsmessagelog.cpp:27
QgsWfs::createTransactionDocument
QDomDocument createTransactionDocument(QgsServerInterface *serverIface, const QgsProject *project, const QString &version, const QgsServerRequest &request)
Create a wfs transaction document.
Definition: qgswfstransaction.cpp:82
QgsField::constraints
QgsFieldConstraints constraints
Definition: qgsfield.h:62
QgsVectorDataProvider
Definition: qgsvectordataprovider.h:58
QgsFeature
Definition: qgsfeature.h:55
qgslogger.h
QgsServerProjectUtils::wfstUpdateLayerIds
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.
Definition: qgsserverprojectutils.cpp:347
QgsFields::at
QgsField at(int i) const
Gets field at particular index (must be in range 0..N-1)
Definition: qgsfields.cpp:163
QgsExpression
Definition: qgsexpression.h:113
QgsFeatureIterator
Definition: qgsfeatureiterator.h:263
QgsMapLayer::type
QgsMapLayerType type() const
Returns the type of the layer.
Definition: qgsmaplayer.cpp:129
QgsServerInterface
Definition: qgsserverinterface.h:60
qgsproject.h
QgsField::type
QVariant::Type type
Definition: qgsfield.h:57
QgsServerResponse
Definition: qgsserverresponse.h:43
QgsServerResponse::setHeader
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...
QgsMapLayer::error
virtual QgsError error() const
Gets current status error.
Definition: qgsmaplayer.cpp:1728
QgsField
Definition: qgsfield.h:49