QGIS API Documentation  3.10.0-A Coruña (6c816b4204)
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( parameters.value( QStringLiteral( "REQUEST_BODY" ) ), 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( QStringLiteral( "; " ) );
189  QString message = errorMessages.join( QStringLiteral( "; " ) );
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  QgsGeometry g = QgsOgcUtils::geometryFromGML( geometryElem );
465  if ( g.isNull() )
466  {
467  action.error = true;
468  action.errorMsg = QStringLiteral( "Geometry from GML error on layer '%1'" ).arg( typeName );
469  vlayer->rollBack();
470  break;
471  }
472  if ( !vlayer->changeGeometry( feature.id(), g ) )
473  {
474  action.error = true;
475  action.errorMsg = QStringLiteral( "Error in change geometry on layer '%1'" ).arg( typeName );
476  vlayer->rollBack();
477  break;
478  }
479  }
480  }
481  if ( action.error )
482  {
483  continue;
484  }
485 #ifdef HAVE_SERVER_PYTHON_PLUGINS
486  // verifying changes
487  if ( accessControl )
488  {
489  fit = vlayer->getFeatures( featureRequest );
490  while ( fit.nextFeature( feature ) )
491  {
492  if ( accessControl && !accessControl->allowToEdit( vlayer, feature ) )
493  {
494  action.error = true;
495  action.errorMsg = QStringLiteral( "Feature modify permission denied on layer '%1'" ).arg( typeName );
496  vlayer->rollBack();
497  break;
498  }
499  }
500  }
501 #endif
502  if ( action.error )
503  {
504  continue;
505  }
506 
507  // Commit the changes of the update elements
508  if ( !vlayer->commitChanges() )
509  {
510  action.error = true;
511  action.errorMsg = QStringLiteral( "Error committing updates: %1" ).arg( vlayer->commitErrors().join( QStringLiteral( "; " ) ) );
512  vlayer->rollBack();
513  continue;
514  }
515  // all the changes are OK!
516  action.error = false;
517 
518  }
519 
520  // perform deletes
521  tdIt = aRequest.deletes.begin();
522  for ( ; tdIt != aRequest.deletes.end(); ++tdIt )
523  {
524  transactionDelete &action = *tdIt;
525  QString typeName = action.typeName;
526 
527  if ( !mapLayerMap.keys().contains( typeName ) )
528  {
529  action.error = true;
530  action.errorMsg = QStringLiteral( "TypeName '%1' unknown" ).arg( typeName );
531  continue;
532  }
533 
534  // get vector layer
535  QgsVectorLayer *vlayer = mapLayerMap[typeName];
536 
537  // verifying specific permissions
538  if ( !wfstDeleteLayerIds.contains( vlayer->id() ) )
539  {
540  action.error = true;
541  action.errorMsg = QStringLiteral( "No permissions to do WFS deletes on layer '%1'" ).arg( typeName );
542  continue;
543  }
544 #ifdef HAVE_SERVER_PYTHON_PLUGINS
545  if ( accessControl && !accessControl->layerDeletePermission( vlayer ) )
546  {
547  action.error = true;
548  action.errorMsg = QStringLiteral( "No permissions to do WFS deletes on layer '%1'" ).arg( typeName );
549  continue;
550  }
551 #endif
552  //get provider
553  QgsVectorDataProvider *provider = vlayer->dataProvider();
554 
555  // verifying specific capabilities
556  int cap = provider->capabilities();
557  if ( !( cap & QgsVectorDataProvider::DeleteFeatures ) )
558  {
559  action.error = true;
560  action.errorMsg = QStringLiteral( "No capabilities to do WFS deletes on layer '%1'" ).arg( typeName );
561  continue;
562  }
563  // start editing
564  vlayer->startEditing();
565 
566  // delete request
567  QgsFeatureRequest featureRequest = action.featureRequest;
568 
569  // expression context
570  QgsExpressionContext expressionContext;
571  expressionContext << QgsExpressionContextUtils::globalScope()
574  featureRequest.setExpressionContext( expressionContext );
575 
576  // verifying feature ids list
577  if ( action.serverFids.isEmpty() )
578  {
579  action.error = true;
580  action.errorMsg = QStringLiteral( "No feature ids to do WFS deletes on layer '%1'" ).arg( typeName );
581  continue;
582  }
583 
584  // update request based on feature ids
585  QgsServerFeatureId::updateFeatureRequestFromServerFids( featureRequest, action.serverFids, provider );
586 
587 #ifdef HAVE_SERVER_PYTHON_PLUGINS
588  if ( accessControl )
589  {
590  accessControl->filterFeatures( vlayer, featureRequest );
591  }
592 #endif
593 
594  // get iterator
595  QgsFeatureIterator fit = vlayer->getFeatures( featureRequest );
596  QgsFeature feature;
597  // get deleted fids
598  QgsFeatureIds fids;
599  while ( fit.nextFeature( feature ) )
600  {
601 #ifdef HAVE_SERVER_PYTHON_PLUGINS
602  if ( accessControl && !accessControl->allowToEdit( vlayer, feature ) )
603  {
604  action.error = true;
605  action.errorMsg = QStringLiteral( "Feature modify permission denied" );
606  vlayer->rollBack();
607  break;
608  }
609 #endif
610  fids << feature.id();
611  }
612  if ( action.error )
613  {
614  continue;
615  }
616  // delete features
617  if ( !vlayer->deleteFeatures( fids ) )
618  {
619  action.error = true;
620  action.errorMsg = QStringLiteral( "Delete features failed on layer '%1'" ).arg( typeName );
621  vlayer->rollBack();
622  continue;
623  }
624 
625  // Commit the changes of the update elements
626  if ( !vlayer->commitChanges() )
627  {
628  action.error = true;
629  action.errorMsg = QStringLiteral( "Error committing deletes: %1" ).arg( vlayer->commitErrors().join( QStringLiteral( "; " ) ) );
630  vlayer->rollBack();
631  continue;
632  }
633  // all the changes are OK!
634  action.error = false;
635  }
636 
637  // perform inserts
638  tiIt = aRequest.inserts.begin();
639  for ( ; tiIt != aRequest.inserts.end(); ++tiIt )
640  {
641  transactionInsert &action = *tiIt;
642  QString typeName = action.typeName;
643 
644  if ( !mapLayerMap.keys().contains( typeName ) )
645  {
646  action.error = true;
647  action.errorMsg = QStringLiteral( "TypeName '%1' unknown" ).arg( typeName );
648  continue;
649  }
650 
651  // get vector layer
652  QgsVectorLayer *vlayer = mapLayerMap[typeName];
653 
654  // verifying specific permissions
655  if ( !wfstInsertLayerIds.contains( vlayer->id() ) )
656  {
657  action.error = true;
658  action.errorMsg = QStringLiteral( "No permissions to do WFS inserts on layer '%1'" ).arg( typeName );
659  continue;
660  }
661 #ifdef HAVE_SERVER_PYTHON_PLUGINS
662  if ( accessControl && !accessControl->layerInsertPermission( vlayer ) )
663  {
664  action.error = true;
665  action.errorMsg = QStringLiteral( "No permissions to do WFS inserts on layer '%1'" ).arg( typeName );
666  continue;
667  }
668 #endif
669  //get provider
670  QgsVectorDataProvider *provider = vlayer->dataProvider();
671 
672  // verifying specific capabilities
673  int cap = provider->capabilities();
674  if ( !( cap & QgsVectorDataProvider::AddFeatures ) )
675  {
676  action.error = true;
677  action.errorMsg = QStringLiteral( "No capabilities to do WFS inserts on layer '%1'" ).arg( typeName );
678  continue;
679  }
680 
681  // start editing
682  vlayer->startEditing();
683 
684  // get inserting features
685  QgsFeatureList featureList;
686  try
687  {
688  featureList = featuresFromGML( action.featureNodeList, provider );
689  }
690  catch ( QgsOgcServiceException &ex )
691  {
692  action.error = true;
693  action.errorMsg = QStringLiteral( "%1 '%2'" ).arg( ex.message() ).arg( typeName );
694  continue;
695  }
696 
697  if ( featureList.empty() )
698  {
699  action.error = true;
700  action.errorMsg = QStringLiteral( "No features to insert in layer '%1'" ).arg( typeName );
701  continue;
702  }
703 
704 #ifdef HAVE_SERVER_PYTHON_PLUGINS
705  // control features
706  if ( accessControl )
707  {
708  QgsFeatureList::iterator featureIt = featureList.begin();
709  while ( featureIt != featureList.end() )
710  {
711  if ( !accessControl->allowToEdit( vlayer, *featureIt ) )
712  {
713  action.error = true;
714  action.errorMsg = QStringLiteral( "Feature modify permission denied on layer '%1'" ).arg( typeName );
715  vlayer->rollBack();
716  break;
717  }
718  featureIt++;
719  }
720  }
721 #endif
722  if ( action.error )
723  {
724  continue;
725  }
726 
727  // perform add features
728  if ( !provider->addFeatures( featureList ) )
729  {
730  action.error = true;
731  action.errorMsg = QStringLiteral( "Insert features failed on layer '%1'" ).arg( typeName );
732  if ( provider ->hasErrors() )
733  {
734  provider->clearErrors();
735  }
736  vlayer->rollBack();
737  continue;
738  }
739 
740  // Commit the changes of the update elements
741  if ( !vlayer->commitChanges() )
742  {
743  action.error = true;
744  action.errorMsg = QStringLiteral( "Error committing inserts: %1" ).arg( vlayer->commitErrors().join( QStringLiteral( "; " ) ) );
745  vlayer->rollBack();
746  continue;
747  }
748  // all changes are OK!
749  action.error = false;
750 
751  // Get the Feature Ids of the inserted feature
752  QgsAttributeList pkAttributes = provider->pkAttributeIndexes();
753  for ( const QgsFeature &feat : qgis::as_const( featureList ) )
754  {
755  action.insertFeatureIds << QStringLiteral( "%1.%2" ).arg( typeName, QgsServerFeatureId::getServerFid( feat, pkAttributes ) );
756  }
757  }
758 
759  //force restoration of original layer filters
760  filterRestorer.reset();
761  }
762 
763  QgsFeatureList featuresFromGML( QDomNodeList featureNodeList, QgsVectorDataProvider *provider )
764  {
765  // Store the inserted features
766  QgsFeatureList featList;
767 
768  // Get Layer Field Information
769  QgsFields fields = provider->fields();
770  const QMap<QString, int> fieldMap = provider->fieldNameMap();
771  QMap<QString, int>::const_iterator fieldMapIt;
772 
773  for ( int i = 0; i < featureNodeList.count(); i++ )
774  {
775  QgsFeature feat( fields );
776 
777  QDomElement featureElem = featureNodeList.at( i ).toElement();
778  QDomNode currentAttributeChild = featureElem.firstChild();
779  bool conversionSuccess = true;
780 
781  while ( !currentAttributeChild.isNull() )
782  {
783  QDomElement currentAttributeElement = currentAttributeChild.toElement();
784  QString attrName = currentAttributeElement.localName();
785 
786  if ( attrName != QLatin1String( "boundedBy" ) )
787  {
788  if ( attrName != QLatin1String( "geometry" ) ) //a normal attribute
789  {
790  fieldMapIt = fieldMap.find( attrName );
791  if ( fieldMapIt == fieldMap.constEnd() )
792  {
793  continue;
794  }
795 
796  QgsField field = fields.at( fieldMapIt.value() );
797  QString attrValue = currentAttributeElement.text();
798  int attrType = field.type();
799 
800  QgsMessageLog::logMessage( QStringLiteral( "attr: name=%1 idx=%2 value=%3" ).arg( attrName ).arg( fieldMapIt.value() ).arg( attrValue ) );
801 
802  if ( attrType == QVariant::Int )
803  feat.setAttribute( fieldMapIt.value(), attrValue.toInt( &conversionSuccess ) );
804  else if ( attrType == QVariant::Double )
805  feat.setAttribute( fieldMapIt.value(), attrValue.toDouble( &conversionSuccess ) );
806  else
807  feat.setAttribute( fieldMapIt.value(), attrValue );
808 
809  if ( !conversionSuccess )
810  {
811  throw QgsRequestNotWellFormedException( QStringLiteral( "Property conversion error on layer insert" ) );
812  }
813  }
814  else //a geometry attribute
815  {
816  QgsGeometry g = QgsOgcUtils::geometryFromGML( currentAttributeElement );
817  if ( g.isNull() )
818  {
819  throw QgsRequestNotWellFormedException( QStringLiteral( "Geometry from GML error on layer insert" ) );
820  }
821  feat.setGeometry( g );
822  }
823  }
824  currentAttributeChild = currentAttributeChild.nextSibling();
825  }
826  // update feature list
827  featList << feat;
828  }
829  return featList;
830  }
831 
833  {
834  if ( !parameters.contains( QStringLiteral( "OPERATION" ) ) )
835  {
836  throw QgsRequestNotWellFormedException( QStringLiteral( "OPERATION parameter is mandatory" ) );
837  }
838  if ( parameters.value( QStringLiteral( "OPERATION" ) ).toUpper() != QStringLiteral( "DELETE" ) )
839  {
840  throw QgsRequestNotWellFormedException( QStringLiteral( "Only DELETE value is defined for OPERATION parameter" ) );
841  }
842 
843  // Verifying parameters mutually exclusive
844  if ( ( parameters.contains( QStringLiteral( "FEATUREID" ) )
845  && ( parameters.contains( QStringLiteral( "FILTER" ) ) || parameters.contains( QStringLiteral( "BBOX" ) ) ) )
846  || ( parameters.contains( QStringLiteral( "FILTER" ) )
847  && ( parameters.contains( QStringLiteral( "FEATUREID" ) ) || parameters.contains( QStringLiteral( "BBOX" ) ) ) )
848  || ( parameters.contains( QStringLiteral( "BBOX" ) )
849  && ( parameters.contains( QStringLiteral( "FEATUREID" ) ) || parameters.contains( QStringLiteral( "FILTER" ) ) ) )
850  )
851  {
852  throw QgsRequestNotWellFormedException( QStringLiteral( "FEATUREID FILTER and BBOX parameters are mutually exclusive" ) );
853  }
854 
855  transactionRequest request;
856 
857  QStringList typeNameList;
858  // parse FEATUREID
859  if ( parameters.contains( QStringLiteral( "FEATUREID" ) ) )
860  {
861  QStringList fidList = parameters.value( QStringLiteral( "FEATUREID" ) ).split( ',' );
862 
863  QMap<QString, QStringList> fidsMap;
864 
865  QStringList::const_iterator fidIt = fidList.constBegin();
866  for ( ; fidIt != fidList.constEnd(); ++fidIt )
867  {
868  // Get FeatureID
869  QString fid = *fidIt;
870  fid = fid.trimmed();
871  // testing typename in the WFS featureID
872  if ( !fid.contains( '.' ) )
873  {
874  throw QgsRequestNotWellFormedException( QStringLiteral( "FEATUREID has to have TYPENAME in the values" ) );
875  }
876 
877  QString typeName = fid.section( '.', 0, 0 );
878  fid = fid.section( '.', 1, 1 );
879  if ( !typeNameList.contains( typeName ) )
880  {
881  typeNameList << typeName;
882  }
883 
884  QStringList fids;
885  if ( fidsMap.contains( typeName ) )
886  {
887  fids = fidsMap.value( typeName );
888  }
889  fids.append( fid );
890  fidsMap.insert( typeName, fids );
891  }
892 
893  QMap<QString, QStringList>::const_iterator fidsMapIt = fidsMap.constBegin();
894  while ( fidsMapIt != fidsMap.constEnd() )
895  {
896  transactionDelete action;
897  action.typeName = fidsMapIt.key();
898 
899  action.serverFids = fidsMapIt.value();
901 
902  request.deletes.append( action );
903  }
904  return request;
905  }
906 
907  if ( !parameters.contains( QStringLiteral( "TYPENAME" ) ) )
908  {
909  throw QgsRequestNotWellFormedException( QStringLiteral( "TYPENAME is mandatory except if FEATUREID is used" ) );
910  }
911 
912  typeNameList = parameters.value( QStringLiteral( "TYPENAME" ) ).split( ',' );
913 
914  // Create actions based on TypeName
915  QStringList::const_iterator typeNameIt = typeNameList.constBegin();
916  for ( ; typeNameIt != typeNameList.constEnd(); ++typeNameIt )
917  {
918  QString typeName = *typeNameIt;
919  typeName = typeName.trimmed();
920 
921  transactionDelete action;
922  action.typeName = typeName;
923 
924  request.deletes.append( action );
925  }
926 
927  // Manage extra parameter exp_filter
928  if ( parameters.contains( QStringLiteral( "EXP_FILTER" ) ) )
929  {
930  QString expFilterName = parameters.value( QStringLiteral( "EXP_FILTER" ) );
931  QStringList expFilterList;
932  QRegExp rx( "\\(([^()]+)\\)" );
933  if ( rx.indexIn( expFilterName, 0 ) == -1 )
934  {
935  expFilterList << expFilterName;
936  }
937  else
938  {
939  int pos = 0;
940  while ( ( pos = rx.indexIn( expFilterName, pos ) ) != -1 )
941  {
942  expFilterList << rx.cap( 1 );
943  pos += rx.matchedLength();
944  }
945  }
946 
947  // Verifying the 1:1 mapping between TYPENAME and EXP_FILTER but without exception
948  if ( request.deletes.size() == expFilterList.size() )
949  {
950  // set feature request filter expression based on filter element
951  QList<transactionDelete>::iterator dIt = request.deletes.begin();
952  QStringList::const_iterator expFilterIt = expFilterList.constBegin();
953  for ( ; dIt != request.deletes.end(); ++dIt )
954  {
955  transactionDelete &action = *dIt;
956  // Get Filter for this typeName
957  QString expFilter;
958  if ( expFilterIt != expFilterList.constEnd() )
959  {
960  expFilter = *expFilterIt;
961  }
962  std::shared_ptr<QgsExpression> filter( new QgsExpression( expFilter ) );
963  if ( filter )
964  {
965  if ( filter->hasParserError() )
966  {
967  QgsMessageLog::logMessage( filter->parserErrorString() );
968  }
969  else
970  {
971  if ( filter->needsGeometry() )
972  {
974  }
975  action.featureRequest.setFilterExpression( filter->expression() );
976  }
977  }
978  }
979  }
980  else
981  {
982  QgsMessageLog::logMessage( "There has to be a 1:1 mapping between each element in a TYPENAME and the EXP_FILTER list" );
983  }
984  }
985 
986  if ( parameters.contains( QStringLiteral( "BBOX" ) ) )
987  {
988  // get bbox value
989  QString bbox = parameters.value( QStringLiteral( "BBOX" ) );
990  if ( bbox.isEmpty() )
991  {
992  throw QgsRequestNotWellFormedException( QStringLiteral( "BBOX parameter is empty" ) );
993  }
994 
995  // get bbox corners
996  QStringList corners = bbox.split( ',' );
997  if ( corners.size() != 4 )
998  {
999  throw QgsRequestNotWellFormedException( QStringLiteral( "BBOX has to be composed of 4 elements: '%1'" ).arg( bbox ) );
1000  }
1001 
1002  // convert corners to double
1003  double d[4];
1004  bool ok;
1005  for ( int i = 0; i < 4; i++ )
1006  {
1007  corners[i].replace( ' ', '+' );
1008  d[i] = corners[i].toDouble( &ok );
1009  if ( !ok )
1010  {
1011  throw QgsRequestNotWellFormedException( QStringLiteral( "BBOX has to be composed of 4 double: '%1'" ).arg( bbox ) );
1012  }
1013  }
1014  // create extent
1015  QgsRectangle extent( d[0], d[1], d[2], d[3] );
1016 
1017  // set feature request filter rectangle
1018  QList<transactionDelete>::iterator dIt = request.deletes.begin();
1019  for ( ; dIt != request.deletes.end(); ++dIt )
1020  {
1021  transactionDelete &action = *dIt;
1022  action.featureRequest.setFilterRect( extent );
1023  }
1024  return request;
1025  }
1026  else if ( parameters.contains( QStringLiteral( "FILTER" ) ) )
1027  {
1028  QString filterName = parameters.value( QStringLiteral( "FILTER" ) );
1029  QStringList filterList;
1030  QRegExp rx( "\\(([^()]+)\\)" );
1031  if ( rx.indexIn( filterName, 0 ) == -1 )
1032  {
1033  filterList << filterName;
1034  }
1035  else
1036  {
1037  int pos = 0;
1038  while ( ( pos = rx.indexIn( filterName, pos ) ) != -1 )
1039  {
1040  filterList << rx.cap( 1 );
1041  pos += rx.matchedLength();
1042  }
1043  }
1044 
1045  // Verifying the 1:1 mapping between TYPENAME and FILTER
1046  if ( request.deletes.size() != filterList.size() )
1047  {
1048  throw QgsRequestNotWellFormedException( QStringLiteral( "There has to be a 1:1 mapping between each element in a TYPENAME and the FILTER list" ) );
1049  }
1050 
1051  // set feature request filter expression based on filter element
1052  QList<transactionDelete>::iterator dIt = request.deletes.begin();
1053  QStringList::const_iterator filterIt = filterList.constBegin();
1054  for ( ; dIt != request.deletes.end(); ++dIt )
1055  {
1056  transactionDelete &action = *dIt;
1057 
1058  // Get Filter for this typeName
1059  QDomDocument filter;
1060  if ( filterIt != filterList.constEnd() )
1061  {
1062  QString errorMsg;
1063  if ( !filter.setContent( *filterIt, true, &errorMsg ) )
1064  {
1065  throw QgsRequestNotWellFormedException( QStringLiteral( "error message: %1. The XML string was: %2" ).arg( errorMsg, *filterIt ) );
1066  }
1067  }
1068 
1069  QDomElement filterElem = filter.firstChildElement();
1070  QStringList serverFids;
1071  action.featureRequest = parseFilterElement( action.typeName, filterElem, serverFids, project );
1072  action.serverFids = serverFids;
1073 
1074  if ( filterIt != filterList.constEnd() )
1075  {
1076  ++filterIt;
1077  }
1078  }
1079  return request;
1080  }
1081 
1082  return request;
1083  }
1084 
1085  transactionRequest parseTransactionRequestBody( QDomElement &docElem, const QgsProject *project )
1086  {
1087  transactionRequest request;
1088 
1089  QDomNodeList docChildNodes = docElem.childNodes();
1090 
1091  QDomElement actionElem;
1092  QString actionName;
1093 
1094  for ( int i = docChildNodes.count(); 0 < i; --i )
1095  {
1096  actionElem = docChildNodes.at( i - 1 ).toElement();
1097  actionName = actionElem.localName();
1098 
1099  if ( actionName == QLatin1String( "Insert" ) )
1100  {
1101  transactionInsert action = parseInsertActionElement( actionElem );
1102  request.inserts.append( action );
1103  }
1104  else if ( actionName == QLatin1String( "Update" ) )
1105  {
1106  transactionUpdate action = parseUpdateActionElement( actionElem, project );
1107  request.updates.append( action );
1108  }
1109  else if ( actionName == QLatin1String( "Delete" ) )
1110  {
1111  transactionDelete action = parseDeleteActionElement( actionElem, project );
1112  request.deletes.append( action );
1113  }
1114  }
1115 
1116  return request;
1117  }
1118 
1119  transactionDelete parseDeleteActionElement( QDomElement &actionElem, const QgsProject *project )
1120  {
1121  QString typeName = actionElem.attribute( QStringLiteral( "typeName" ) );
1122  if ( typeName.contains( ':' ) )
1123  typeName = typeName.section( ':', 1, 1 );
1124 
1125  QDomElement filterElem = actionElem.firstChild().toElement();
1126  if ( filterElem.tagName() != QLatin1String( "Filter" ) )
1127  {
1128  throw QgsRequestNotWellFormedException( QStringLiteral( "Delete action element first child is not Filter" ) );
1129  }
1130 
1131  QStringList serverFids;
1132  QgsFeatureRequest featureRequest = parseFilterElement( typeName, filterElem, serverFids, project );
1133 
1134  transactionDelete action;
1135  action.typeName = typeName;
1136  action.featureRequest = featureRequest;
1137  action.serverFids = serverFids;
1138  action.error = false;
1139 
1140  if ( actionElem.hasAttribute( QStringLiteral( "handle" ) ) )
1141  {
1142  action.handle = actionElem.attribute( QStringLiteral( "handle" ) );
1143  }
1144 
1145  return action;
1146  }
1147 
1148  transactionUpdate parseUpdateActionElement( QDomElement &actionElem, const QgsProject *project )
1149  {
1150  QString typeName = actionElem.attribute( QStringLiteral( "typeName" ) );
1151  if ( typeName.contains( ':' ) )
1152  typeName = typeName.section( ':', 1, 1 );
1153 
1154  QDomNodeList propertyNodeList = actionElem.elementsByTagName( QStringLiteral( "Property" ) );
1155  if ( propertyNodeList.isEmpty() )
1156  {
1157  throw QgsRequestNotWellFormedException( QStringLiteral( "Update action element must have one or more Property element" ) );
1158  }
1159 
1160  QMap<QString, QString> propertyMap;
1161  QDomElement propertyElem;
1162  QDomElement nameElem;
1163  QDomElement valueElem;
1164  QDomElement geometryElem;
1165 
1166  for ( int l = 0; l < propertyNodeList.count(); ++l )
1167  {
1168  propertyElem = propertyNodeList.at( l ).toElement();
1169  nameElem = propertyElem.elementsByTagName( QStringLiteral( "Name" ) ).at( 0 ).toElement();
1170  valueElem = propertyElem.elementsByTagName( QStringLiteral( "Value" ) ).at( 0 ).toElement();
1171  if ( nameElem.text() != QLatin1String( "geometry" ) )
1172  {
1173  propertyMap.insert( nameElem.text(), valueElem.text() );
1174  }
1175  else
1176  {
1177  geometryElem = valueElem;
1178  }
1179  }
1180 
1181  QDomNodeList filterNodeList = actionElem.elementsByTagName( QStringLiteral( "Filter" ) );
1182  QgsFeatureRequest featureRequest;
1183  QStringList serverFids;
1184  if ( filterNodeList.size() != 0 )
1185  {
1186  QDomElement filterElem = filterNodeList.at( 0 ).toElement();
1187  featureRequest = parseFilterElement( typeName, filterElem, serverFids, project );
1188  }
1189 
1190  transactionUpdate action;
1191  action.typeName = typeName;
1192  action.propertyMap = propertyMap;
1193  action.geometryElement = geometryElem;
1194  action.featureRequest = featureRequest;
1195  action.serverFids = serverFids;
1196  action.error = false;
1197 
1198  if ( actionElem.hasAttribute( QStringLiteral( "handle" ) ) )
1199  {
1200  action.handle = actionElem.attribute( QStringLiteral( "handle" ) );
1201  }
1202 
1203  return action;
1204  }
1205 
1206  transactionInsert parseInsertActionElement( QDomElement &actionElem )
1207  {
1208  QDomNodeList featureNodeList = actionElem.childNodes();
1209  if ( featureNodeList.size() != 1 )
1210  {
1211  throw QgsRequestNotWellFormedException( QStringLiteral( "Insert action element must have one or more child node" ) );
1212  }
1213 
1214  QString typeName;
1215  for ( int i = 0; i < featureNodeList.count(); ++i )
1216  {
1217  QString tempTypeName = featureNodeList.at( i ).toElement().localName();
1218  if ( tempTypeName.contains( ':' ) )
1219  tempTypeName = tempTypeName.section( ':', 1, 1 );
1220 
1221  if ( typeName.isEmpty() )
1222  {
1223  typeName = tempTypeName;
1224  }
1225  else if ( tempTypeName != typeName )
1226  {
1227  throw QgsRequestNotWellFormedException( QStringLiteral( "Insert action element must have one typename features" ) );
1228  }
1229  }
1230 
1231  transactionInsert action;
1232  action.typeName = typeName;
1233  action.featureNodeList = featureNodeList;
1234  action.error = false;
1235 
1236  if ( actionElem.hasAttribute( QStringLiteral( "handle" ) ) )
1237  {
1238  action.handle = actionElem.attribute( QStringLiteral( "handle" ) );
1239  }
1240 
1241  return action;
1242  }
1243 
1244  namespace
1245  {
1246 
1247  void addTransactionResult( QDomDocument &responseDoc, QDomElement &responseElem, const QString &status,
1248  const QString &locator, const QString &message )
1249  {
1250  QDomElement trElem = responseDoc.createElement( QStringLiteral( "TransactionResult" ) );
1251  QDomElement stElem = responseDoc.createElement( QStringLiteral( "Status" ) );
1252  QDomElement successElem = responseDoc.createElement( status );
1253  stElem.appendChild( successElem );
1254  trElem.appendChild( stElem );
1255  responseElem.appendChild( trElem );
1256 
1257  if ( !locator.isEmpty() )
1258  {
1259  QDomElement locElem = responseDoc.createElement( QStringLiteral( "Locator" ) );
1260  locElem.appendChild( responseDoc.createTextNode( locator ) );
1261  trElem.appendChild( locElem );
1262  }
1263 
1264  if ( !message.isEmpty() )
1265  {
1266  QDomElement mesElem = responseDoc.createElement( QStringLiteral( "Message" ) );
1267  mesElem.appendChild( responseDoc.createTextNode( message ) );
1268  trElem.appendChild( mesElem );
1269  }
1270  }
1271 
1272  }
1273 
1274  } // namespace v1_0_0
1275 } // namespace QgsWfs
1276 
1277 
Class for parsing and evaluation of expressions (formerly called "search strings").
QgsFeatureId id
Definition: qgsfeature.h:64
bool layerInsertPermission(const QgsVectorLayer *layer) const
Returns the layer insert right.
Wrapper for iterator of features from vector data provider or vector layer.
virtual void setHeader(const QString &key, const QString &value)=0
Set Header entry Add Header entry to the response Note that it is usually an error to set Header afte...
A rectangle specified with double values.
Definition: qgsrectangle.h:41
Base class for all map layer types.
Definition: qgsmaplayer.h:79
SERVER_EXPORT QStringList wfstUpdateLayerIds(const QgsProject &project)
Returns the Layer ids list defined in a QGIS project as published as WFS-T with update capabilities...
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeatureid.h:34
transactionRequest parseTransactionRequestBody(QDomElement &docElem, const QgsProject *project)
Transform RequestBody root element to getFeatureRequest.
virtual QgsVectorDataProvider::Capabilities capabilities() const
Returns flags containing the supported capabilities.
QMap< QString, int > fieldNameMap() const
Returns a map where the key is the name of the field and the value is its index.
QgsMapLayerType type() const
Returns the type of the layer.
QString name
Definition: qgsfield.h:58
SERVER_EXPORT QString getServerFid(const QgsFeature &feature, const QgsAttributeList &pkAttributes)
Returns the feature id based on primary keys.
bool addFeatures(QgsFeatureList &flist, QgsFeatureSink::Flags flags=nullptr) override
Adds a list of features to the sink.
QList< QgsFeature > QgsFeatureList
Definition: qgsfeature.h:571
bool commitChanges()
Attempts to commit to the underlying data provider any buffered changes made since the last to call t...
bool startEditing()
Makes the layer editable.
Exception base class for service exceptions.
virtual QgsAttributeList pkAttributeIndexes() const
Returns list of indexes of fields that make up the primary key.
bool deleteFeatures(const QgsFeatureIds &fids)
Deletes a set of features from the layer (but does not commit it)
Container of fields for a vector layer.
Definition: qgsfields.h:42
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:122
bool setAttribute(int field, const QVariant &attr)
Set an attribute&#39;s value by field index.
Definition: qgsfeature.cpp:211
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:55
QString message() const
Returns the exception message.
bool layerDeletePermission(const QgsVectorLayer *layer) const
Returns the layer delete right.
virtual void write(const QString &data)
Write string This is a convenient method that will write directly to the underlying I/O device...
QString layerTypeName(const QgsMapLayer *layer)
Returns typename from vector layer.
Definition: qgswfsutils.cpp:71
QgsField at(int i) const
Gets field at particular index (must be in range 0..N-1)
Definition: qgsfields.cpp:163
static void applyAccessControlLayerFilters(const QgsAccessControl *accessControl, QgsMapLayer *mapLayer, QHash< QgsMapLayer *, QString > &originalLayerFilters)
Apply filter from AccessControl.
QgsFeatureRequest & setExpressionContext(const QgsExpressionContext &context)
Sets the expression context used to evaluate filter expressions.
transactionRequest parseTransactionParameters(QgsServerRequest::Parameters parameters, const QgsProject *project)
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context...
QString id() const
Returns the layer&#39;s unique ID, which is used to access this layer from QgsProject.
bool changeGeometry(QgsFeatureId fid, QgsGeometry &geometry, bool skipDefaultValue=false)
Changes a feature&#39;s geometry within the layer&#39;s edit buffer (but does not immediately commit the chan...
bool rollBack(bool deleteBuffer=true)
Stops a current editing operation and discards any uncommitted edits.
Exception thrown in case of malformed request.
SERVER_EXPORT QStringList wfstDeleteLayerIds(const QgsProject &project)
Returns the Layer ids list defined in a QGIS project as published as WFS-T with delete capabilities...
QgsFields fields() const override=0
Returns the fields associated with this data provider.
const QString & typeName
WMS implementation.
Definition: qgswfs.cpp:35
QgsServerRequest::Parameters parameters() const
Returns a map of query parameters with keys converted to uppercase.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
This class wraps a request for features to a vector layer (or directly its vector data provider)...
const QString WFS_NAMESPACE
Definition: qgswfsutils.h:71
QgsFeatureRequest & setFilterRect(const QgsRectangle &rectangle)
Sets the rectangle from which features will be taken.
QgsFeatureRequest parseFilterElement(const QString &typeName, QDomElement &filterElem, const QgsProject *project)
Transform a Filter element to a feature request.
Allows modifications of geometries.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts, annotations, canvases, etc.
Definition: qgsproject.h:89
void clearErrors()
Clear recorded errors.
transactionInsert parseInsertActionElement(QDomElement &actionElem)
Transform Insert element to transactionInsert.
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:48
transactionUpdate parseUpdateActionElement(QDomElement &actionElem, const QgsProject *project)
Transform Update element to transactionUpdate.
SERVER_EXPORT QgsFeatureRequest updateFeatureRequestFromServerFids(QgsFeatureRequest &featureRequest, const QStringList &serverFids, const QgsVectorDataProvider *provider)
Returns the feature request based on feature ids build with primary keys.
const QString OGC_NAMESPACE
Definition: qgswfsutils.h:73
QStringList commitErrors() const
Returns a list containing any error messages generated when attempting to commit changes to the layer...
QgsServerRequest Class defining request interface passed to services QgsService::executeRequest() met...
static QgsGeometry geometryFromGML(const QString &xmlString)
Static method that creates geometry from GML.
QgsServerInterface Class defining interfaces exposed by QGIS Server and made available to plugins...
QgsFieldConstraints constraints
Definition: qgsfield.h:61
void writeTransaction(QgsServerInterface *serverIface, const QgsProject *project, const QString &version, const QgsServerRequest &request, QgsServerResponse &response)
Output WFS transaction response.
SERVER_EXPORT QStringList wfsLayerIds(const QgsProject &project)
Returns the Layer ids list defined in a QGIS project as published in WFS.
void setGeometry(const QgsGeometry &geometry)
Set the feature&#39;s geometry.
Definition: qgsfeature.cpp:137
RAII class to restore layer filters on destruction.
A helper class that centralizes restrictions given by all the access control filter plugins...
QgsFeatureList featuresFromGML(QDomNodeList featureNodeList, QgsVectorDataProvider *provider)
Transform GML feature nodes to features.
void filterFeatures(const QgsVectorLayer *layer, QgsFeatureRequest &filterFeatures) const override
Filter the features of the layer.
Q_INVOKABLE QgsMapLayer * mapLayer(const QString &layerId) const
Retrieve a pointer to a registered layer by layer ID.
SERVER_EXPORT QStringList wfstInsertLayerIds(const QgsProject &project)
Returns the Layer ids list defined in a QGIS project as published as WFS-T with insert capabilities...
Exception thrown when data access violates access controls.
bool layerUpdatePermission(const QgsVectorLayer *layer) const
Returns the layer update right.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
QgsServerResponse Class defining response interface passed to services QgsService::executeRequest() m...
bool changeAttributeValue(QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue=QVariant(), bool skipDefaultValues=false)
Changes an attribute value for a feature (but does not immediately commit the changes).
virtual QgsAccessControl * accessControls() const =0
Gets the registered access control filters.
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer&#39;s data provider, it may be nullptr.
QList< int > QgsAttributeList
Definition: qgsfield.h:27
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
bool nextFeature(QgsFeature &f)
This is the base class for vector data providers.
Represents a vector layer which manages a vector based data sets.
void performTransaction(transactionRequest &aRequest, QgsServerInterface *serverIface, const QgsProject *project)
Perform the transaction.
Allows modification of attribute values.
QVariant::Type type
Definition: qgsfield.h:56
QDomDocument createTransactionDocument(QgsServerInterface *serverIface, const QgsProject *project, const QString &version, const QgsServerRequest &request)
Create a wfs transaction document.
transactionDelete parseDeleteActionElement(QDomElement &actionElem, const QgsProject *project)
Transform Delete element to transactionDelete.
QgsFeatureRequest & setFlags(QgsFeatureRequest::Flags flags)
Sets flags that affect how features will be fetched.
QMap< QString, QString > Parameters
bool allowToEdit(const QgsVectorLayer *layer, const QgsFeature &feature) const
Are we authorized to modify the following geometry.