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