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