QGIS API Documentation 3.41.0-Master (02257426e5a)
Loading...
Searching...
No Matches
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 std::unique_ptr<QgsOWSServerFilterRestorer> filterRestorer( new 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 {
391 accessControl->filterFeatures( vlayer, featureRequest );
392 }
393#endif
394 // get iterator
395 QgsFeatureIterator fit = vlayer->getFeatures( featureRequest );
396 QgsFeature feature;
397 int totalUpdated = 0;
398 // get action properties
399 QMap<QString, QString> propertyMap = action.propertyMap;
400 QDomElement geometryElem = action.geometryElement;
401 // get field information
402 QgsFields fields = provider->fields();
403 const QMap<QString, int> fieldMap = provider->fieldNameMap();
404 QMap<QString, int>::const_iterator fieldMapIt;
405 QString fieldName;
406 bool conversionSuccess;
407 // Update the features
408 while ( fit.nextFeature( feature ) )
409 {
410#ifdef HAVE_SERVER_PYTHON_PLUGINS
411 if ( accessControl && !accessControl->allowToEdit( vlayer, feature ) )
412 {
413 action.error = true;
414 action.errorMsg = QStringLiteral( "Feature modify permission denied on layer '%1'" ).arg( typeName );
415 vlayer->rollBack();
416 break;
417 }
418#endif
419 QMap<QString, QString>::const_iterator it = propertyMap.constBegin();
420 for ( ; it != propertyMap.constEnd(); ++it )
421 {
422 fieldName = it.key();
423 fieldMapIt = fieldMap.find( fieldName );
424 if ( fieldMapIt == fieldMap.constEnd() )
425 {
426 continue;
427 }
428 QgsField field = fields.at( fieldMapIt.value() );
429 QVariant value = it.value();
430 if ( QgsVariantUtils::isNull( value ) )
431 {
433 {
434 action.error = true;
435 action.errorMsg = QStringLiteral( "NOT NULL constraint error on layer '%1', field '%2'" ).arg( typeName, field.name() );
436 vlayer->rollBack();
437 break;
438 }
439 }
440 else // Not NULL
441 {
442 if ( field.type() == QMetaType::Type::Int )
443 {
444 value = it.value().toInt( &conversionSuccess );
445 if ( !conversionSuccess )
446 {
447 action.error = true;
448 action.errorMsg = QStringLiteral( "Property conversion error on layer '%1'" ).arg( typeName );
449 vlayer->rollBack();
450 break;
451 }
452 }
453 else if ( field.type() == QMetaType::Type::Double )
454 {
455 value = it.value().toDouble( &conversionSuccess );
456 if ( !conversionSuccess )
457 {
458 action.error = true;
459 action.errorMsg = QStringLiteral( "Property conversion error on layer '%1'" ).arg( typeName );
460 vlayer->rollBack();
461 break;
462 }
463 }
464 else if ( field.type() == QMetaType::Type::LongLong )
465 {
466 value = it.value().toLongLong( &conversionSuccess );
467 if ( !conversionSuccess )
468 {
469 action.error = true;
470 action.errorMsg = QStringLiteral( "Property conversion error on layer '%1'" ).arg( typeName );
471 vlayer->rollBack();
472 break;
473 }
474 }
475 }
476 vlayer->changeAttributeValue( feature.id(), fieldMapIt.value(), value );
477 }
478 if ( action.error )
479 {
480 break;
481 }
482
483 if ( !geometryElem.isNull() )
484 {
485 QgsGeometry g = QgsOgcUtils::geometryFromGML( geometryElem );
486 if ( g.isNull() )
487 {
488 action.error = true;
489 action.errorMsg = QStringLiteral( "Geometry from GML error on layer '%1'" ).arg( typeName );
490 vlayer->rollBack();
491 break;
492 }
493 if ( !vlayer->changeGeometry( feature.id(), g ) )
494 {
495 action.error = true;
496 action.errorMsg = QStringLiteral( "Error in change geometry on layer '%1'" ).arg( typeName );
497 vlayer->rollBack();
498 break;
499 }
500 }
501 totalUpdated += 1;
502 }
503 if ( action.error )
504 {
505 continue;
506 }
507#ifdef HAVE_SERVER_PYTHON_PLUGINS
508 // verifying changes
509 if ( accessControl )
510 {
511 fit = vlayer->getFeatures( featureRequest );
512 while ( fit.nextFeature( feature ) )
513 {
514 if ( accessControl && !accessControl->allowToEdit( vlayer, feature ) )
515 {
516 action.error = true;
517 action.errorMsg = QStringLiteral( "Feature modify permission denied on layer '%1'" ).arg( typeName );
518 vlayer->rollBack();
519 break;
520 }
521 }
522 }
523 if ( action.error )
524 {
525 continue;
526 }
527#endif
528
529 // Commit the changes of the update elements
530 if ( !vlayer->commitChanges() )
531 {
532 action.error = true;
533 action.errorMsg = QStringLiteral( "Error committing updates: %1" ).arg( vlayer->commitErrors().join( QLatin1String( "; " ) ) );
534 vlayer->rollBack();
535 continue;
536 }
537 // all the changes are OK!
538 action.totalUpdated = totalUpdated;
539 action.error = false;
540 }
541
542 // perform deletes
543 tdIt = aRequest.deletes.begin();
544 for ( ; tdIt != aRequest.deletes.end(); ++tdIt )
545 {
546 transactionDelete &action = *tdIt;
547 QString typeName = action.typeName;
548
549 if ( !mapLayerMap.contains( typeName ) )
550 {
551 action.error = true;
552 action.errorMsg = QStringLiteral( "TypeName '%1' unknown" ).arg( typeName );
553 continue;
554 }
555
556 // get vector layer
557 QgsVectorLayer *vlayer = mapLayerMap[typeName];
558
559 // verifying specific permissions
560 if ( !wfstDeleteLayerIds.contains( vlayer->id() ) )
561 {
562 action.error = true;
563 action.errorMsg = QStringLiteral( "No permissions to do WFS deletes on layer '%1'" ).arg( typeName );
564 continue;
565 }
566#ifdef HAVE_SERVER_PYTHON_PLUGINS
567 if ( accessControl && !accessControl->layerDeletePermission( vlayer ) )
568 {
569 action.error = true;
570 action.errorMsg = QStringLiteral( "No permissions to do WFS deletes on layer '%1'" ).arg( typeName );
571 continue;
572 }
573#endif
574 //get provider
575 QgsVectorDataProvider *provider = vlayer->dataProvider();
576
577 // verifying specific capabilities
580 {
581 action.error = true;
582 action.errorMsg = QStringLiteral( "No capabilities to do WFS deletes on layer '%1'" ).arg( typeName );
583 continue;
584 }
585 // start editing
586 vlayer->startEditing();
587
588 // delete request
589 QgsFeatureRequest featureRequest = action.featureRequest;
590
591 // expression context
592 QgsExpressionContext expressionContext;
593 expressionContext << QgsExpressionContextUtils::globalScope()
596 featureRequest.setExpressionContext( expressionContext );
597
598 // verifying feature ids list
599 if ( action.serverFids.isEmpty() )
600 {
601 action.error = true;
602 action.errorMsg = QStringLiteral( "No feature ids to do WFS deletes on layer '%1'" ).arg( typeName );
603 continue;
604 }
605
606 // update request based on feature ids
607 QgsServerFeatureId::updateFeatureRequestFromServerFids( featureRequest, action.serverFids, provider );
608
609#ifdef HAVE_SERVER_PYTHON_PLUGINS
610 if ( accessControl )
611 {
612 accessControl->filterFeatures( vlayer, featureRequest );
613 }
614#endif
615
616 // get iterator
617 QgsFeatureIterator fit = vlayer->getFeatures( featureRequest );
618 QgsFeature feature;
619 // get deleted fids
620 QgsFeatureIds fids;
621 while ( fit.nextFeature( feature ) )
622 {
623#ifdef HAVE_SERVER_PYTHON_PLUGINS
624 if ( accessControl && !accessControl->allowToEdit( vlayer, feature ) )
625 {
626 action.error = true;
627 action.errorMsg = QStringLiteral( "Feature modify permission denied" );
628 vlayer->rollBack();
629 break;
630 }
631#endif
632 fids << feature.id();
633 }
634 if ( action.error )
635 {
636 continue;
637 }
638 // delete features
639 if ( !vlayer->deleteFeatures( fids ) )
640 {
641 action.error = true;
642 action.errorMsg = QStringLiteral( "Delete features failed on layer '%1'" ).arg( typeName );
643 vlayer->rollBack();
644 continue;
645 }
646
647 // Commit the changes of the update elements
648 if ( !vlayer->commitChanges() )
649 {
650 action.error = true;
651 action.errorMsg = QStringLiteral( "Error committing deletes: %1" ).arg( vlayer->commitErrors().join( QLatin1String( "; " ) ) );
652 vlayer->rollBack();
653 continue;
654 }
655 // all the changes are OK!
656 action.totalDeleted = fids.count();
657 action.error = false;
658 }
659
660 // perform inserts
661 tiIt = aRequest.inserts.begin();
662 for ( ; tiIt != aRequest.inserts.end(); ++tiIt )
663 {
664 transactionInsert &action = *tiIt;
665 QString typeName = action.typeName;
666
667 if ( !mapLayerMap.contains( typeName ) )
668 {
669 action.error = true;
670 action.errorMsg = QStringLiteral( "TypeName '%1' unknown" ).arg( typeName );
671 continue;
672 }
673
674 // get vector layer
675 QgsVectorLayer *vlayer = mapLayerMap[typeName];
676
677 // verifying specific permissions
678 if ( !wfstInsertLayerIds.contains( vlayer->id() ) )
679 {
680 action.error = true;
681 action.errorMsg = QStringLiteral( "No permissions to do WFS inserts on layer '%1'" ).arg( typeName );
682 continue;
683 }
684#ifdef HAVE_SERVER_PYTHON_PLUGINS
685 if ( accessControl && !accessControl->layerInsertPermission( vlayer ) )
686 {
687 action.error = true;
688 action.errorMsg = QStringLiteral( "No permissions to do WFS inserts on layer '%1'" ).arg( typeName );
689 continue;
690 }
691#endif
692 //get provider
693 QgsVectorDataProvider *provider = vlayer->dataProvider();
694
695 // verifying specific capabilities
698 {
699 action.error = true;
700 action.errorMsg = QStringLiteral( "No capabilities to do WFS inserts on layer '%1'" ).arg( typeName );
701 continue;
702 }
703
704 // start editing
705 vlayer->startEditing();
706
707 // get inserting features
708 QgsFeatureList featureList;
709 try
710 {
711 featureList = featuresFromGML( action.featureNodeList, vlayer );
712 }
713 catch ( QgsOgcServiceException &ex )
714 {
715 action.error = true;
716 action.errorMsg = QStringLiteral( "%1 '%2'" ).arg( ex.message(), typeName );
717 continue;
718 }
719
720 if ( featureList.empty() )
721 {
722 action.error = true;
723 action.errorMsg = QStringLiteral( "No features to insert in layer '%1'" ).arg( typeName );
724 continue;
725 }
726
727#ifdef HAVE_SERVER_PYTHON_PLUGINS
728 // control features
729 if ( accessControl )
730 {
731 QgsFeatureList::iterator featureIt = featureList.begin();
732 while ( featureIt != featureList.end() )
733 {
734 if ( !accessControl->allowToEdit( vlayer, *featureIt ) )
735 {
736 action.error = true;
737 action.errorMsg = QStringLiteral( "Feature modify permission denied on layer '%1'" ).arg( typeName );
738 vlayer->rollBack();
739 break;
740 }
741 featureIt++;
742 }
743 }
744#endif
745 if ( action.error )
746 {
747 continue;
748 }
749
750 // perform add features
751 if ( !provider->addFeatures( featureList ) )
752 {
753 action.error = true;
754 action.errorMsg = QStringLiteral( "Insert features failed on layer '%1'" ).arg( typeName );
755 if ( provider->hasErrors() )
756 {
757 provider->clearErrors();
758 }
759 vlayer->rollBack();
760 continue;
761 }
762
763 // Commit the changes of the update elements
764 if ( !vlayer->commitChanges() )
765 {
766 action.error = true;
767 action.errorMsg = QStringLiteral( "Error committing inserts: %1" ).arg( vlayer->commitErrors().join( QLatin1String( "; " ) ) );
768 vlayer->rollBack();
769 continue;
770 }
771 // all changes are OK!
772 action.error = false;
773
774 // Get the Feature Ids of the inserted feature
775 QgsAttributeList pkAttributes = provider->pkAttributeIndexes();
776 for ( const QgsFeature &feat : std::as_const( featureList ) )
777 {
778 action.insertFeatureIds << QStringLiteral( "%1.%2" ).arg( typeName, QgsServerFeatureId::getServerFid( feat, pkAttributes ) );
779 }
780 }
781
782 //force restoration of original layer filters
783 filterRestorer.reset();
784 }
785
786 QgsFeatureList featuresFromGML( QDomNodeList featureNodeList, QgsVectorLayer *layer )
787 {
788 // Store the inserted features
789 QgsFeatureList featList;
790
791 const QgsVectorDataProvider *provider { layer->dataProvider() };
792
793 // Get Layer Field Information
794 QgsFields fields = provider->fields();
795 const QMap<QString, int> fieldMap = provider->fieldNameMap();
796 QMap<QString, int>::const_iterator fieldMapIt;
797
798 for ( int i = 0; i < featureNodeList.count(); i++ )
799 {
800 QgsFeature feat( fields );
801
802 QDomElement featureElem = featureNodeList.at( i ).toElement();
803 QDomNode currentAttributeChild = featureElem.firstChild();
804 bool conversionSuccess = true;
805
806 while ( !currentAttributeChild.isNull() )
807 {
808 QDomElement currentAttributeElement = currentAttributeChild.toElement();
809 QString attrName = currentAttributeElement.localName();
810
811 if ( attrName != QLatin1String( "boundedBy" ) )
812 {
813 if ( attrName != QLatin1String( "geometry" ) ) //a normal attribute
814 {
815 fieldMapIt = fieldMap.find( attrName );
816 if ( fieldMapIt == fieldMap.constEnd() )
817 {
818 continue;
819 }
820
821 QgsField field = fields.at( fieldMapIt.value() );
822 QString attrValue = currentAttributeElement.text();
823 int attrType = field.type();
824
825 QgsMessageLog::logMessage( QStringLiteral( "attr: name=%1 idx=%2 value=%3" ).arg( attrName ).arg( fieldMapIt.value() ).arg( attrValue ) );
826
827 if ( attrType == QMetaType::Type::Int )
828 feat.setAttribute( fieldMapIt.value(), attrValue.toInt( &conversionSuccess ) );
829 else if ( attrType == QMetaType::Type::Double )
830 feat.setAttribute( fieldMapIt.value(), attrValue.toDouble( &conversionSuccess ) );
831 else
832 feat.setAttribute( fieldMapIt.value(), attrValue );
833
834 if ( !conversionSuccess )
835 {
836 throw QgsRequestNotWellFormedException( QStringLiteral( "Property conversion error on layer insert" ) );
837 }
838 }
839 else //a geometry attribute
840 {
841 const QgsOgcUtils::Context context { layer, provider->transformContext() };
842 QgsGeometry g = QgsOgcUtils::geometryFromGML( currentAttributeElement, context );
843 if ( g.isNull() )
844 {
845 throw QgsRequestNotWellFormedException( QStringLiteral( "Geometry from GML error on layer insert" ) );
846 }
847 feat.setGeometry( g );
848 }
849 }
850 currentAttributeChild = currentAttributeChild.nextSibling();
851 }
852 // update feature list
853 featList << feat;
854 }
855 return featList;
856 }
857
859 {
860 if ( !parameters.contains( QStringLiteral( "OPERATION" ) ) )
861 {
862 throw QgsRequestNotWellFormedException( QStringLiteral( "OPERATION parameter is mandatory" ) );
863 }
864 if ( parameters.value( QStringLiteral( "OPERATION" ) ).toUpper() != QLatin1String( "DELETE" ) )
865 {
866 throw QgsRequestNotWellFormedException( QStringLiteral( "Only DELETE value is defined for OPERATION parameter" ) );
867 }
868
869 // Verifying parameters mutually exclusive
870 if ( ( parameters.contains( QStringLiteral( "FEATUREID" ) )
871 && ( parameters.contains( QStringLiteral( "FILTER" ) ) || parameters.contains( QStringLiteral( "BBOX" ) ) ) )
872 || ( parameters.contains( QStringLiteral( "FILTER" ) ) && ( parameters.contains( QStringLiteral( "FEATUREID" ) ) || parameters.contains( QStringLiteral( "BBOX" ) ) ) )
873 || ( parameters.contains( QStringLiteral( "BBOX" ) ) && ( parameters.contains( QStringLiteral( "FEATUREID" ) ) || parameters.contains( QStringLiteral( "FILTER" ) ) ) ) )
874 {
875 throw QgsRequestNotWellFormedException( QStringLiteral( "FEATUREID FILTER and BBOX parameters are mutually exclusive" ) );
876 }
877
878 transactionRequest request;
879
880 QStringList typeNameList;
881 // parse FEATUREID
882 if ( parameters.contains( QStringLiteral( "FEATUREID" ) ) )
883 {
884 QStringList fidList = parameters.value( QStringLiteral( "FEATUREID" ) ).split( ',' );
885
886 QMap<QString, QStringList> fidsMap;
887
888 QStringList::const_iterator fidIt = fidList.constBegin();
889 for ( ; fidIt != fidList.constEnd(); ++fidIt )
890 {
891 // Get FeatureID
892 QString fid = *fidIt;
893 fid = fid.trimmed();
894 // testing typename in the WFS featureID
895 if ( !fid.contains( '.' ) )
896 {
897 throw QgsRequestNotWellFormedException( QStringLiteral( "FEATUREID has to have TYPENAME in the values" ) );
898 }
899
900 QString typeName = fid.section( '.', 0, 0 );
901 fid = fid.section( '.', 1, 1 );
902 if ( !typeNameList.contains( typeName ) )
903 {
904 typeNameList << typeName;
905 }
906
907 QStringList fids;
908 if ( fidsMap.contains( typeName ) )
909 {
910 fids = fidsMap.value( typeName );
911 }
912 fids.append( fid );
913 fidsMap.insert( typeName, fids );
914 }
915
916 QMap<QString, QStringList>::const_iterator fidsMapIt = fidsMap.constBegin();
917 while ( fidsMapIt != fidsMap.constEnd() )
918 {
919 transactionDelete action;
920 action.typeName = fidsMapIt.key();
921
922 action.serverFids = fidsMapIt.value();
924
925 request.deletes.append( action );
926 }
927 return request;
928 }
929
930 if ( !parameters.contains( QStringLiteral( "TYPENAME" ) ) )
931 {
932 throw QgsRequestNotWellFormedException( QStringLiteral( "TYPENAME is mandatory except if FEATUREID is used" ) );
933 }
934
935 typeNameList = parameters.value( QStringLiteral( "TYPENAME" ) ).split( ',' );
936
937 // Create actions based on TypeName
938 QStringList::const_iterator typeNameIt = typeNameList.constBegin();
939 for ( ; typeNameIt != typeNameList.constEnd(); ++typeNameIt )
940 {
941 QString typeName = *typeNameIt;
942 typeName = typeName.trimmed();
943
944 transactionDelete action;
945 action.typeName = typeName;
946
947 request.deletes.append( action );
948 }
949
950 // Manage extra parameter exp_filter
951 if ( parameters.contains( QStringLiteral( "EXP_FILTER" ) ) )
952 {
953 QString expFilterName = parameters.value( QStringLiteral( "EXP_FILTER" ) );
954 QStringList expFilterList;
955 const thread_local QRegularExpression rx( "\\(([^()]+)\\)" );
956 QRegularExpressionMatchIterator matchIt = rx.globalMatch( expFilterName );
957 if ( !matchIt.hasNext() )
958 {
959 expFilterList << expFilterName;
960 }
961 else
962 {
963 while ( matchIt.hasNext() )
964 {
965 const QRegularExpressionMatch match = matchIt.next();
966 if ( match.hasMatch() )
967 {
968 QStringList matches = match.capturedTexts();
969 matches.pop_front(); // remove whole match
970 expFilterList.append( matches );
971 }
972 }
973 }
974
975 // Verifying the 1:1 mapping between TYPENAME and EXP_FILTER but without exception
976 if ( request.deletes.size() == expFilterList.size() )
977 {
978 // set feature request filter expression based on filter element
979 QList<transactionDelete>::iterator dIt = request.deletes.begin();
980 QStringList::const_iterator expFilterIt = expFilterList.constBegin();
981 for ( ; dIt != request.deletes.end(); ++dIt )
982 {
983 transactionDelete &action = *dIt;
984 // Get Filter for this typeName
985 QString expFilter;
986 if ( expFilterIt != expFilterList.constEnd() )
987 {
988 expFilter = *expFilterIt;
989 }
990 std::shared_ptr<QgsExpression> filter( new QgsExpression( expFilter ) );
991 if ( filter )
992 {
993 if ( filter->hasParserError() )
994 {
995 QgsMessageLog::logMessage( filter->parserErrorString() );
996 }
997 else
998 {
999 if ( filter->needsGeometry() )
1000 {
1002 }
1003 action.featureRequest.setFilterExpression( filter->expression() );
1004 }
1005 }
1006 }
1007 }
1008 else
1009 {
1010 QgsMessageLog::logMessage( "There has to be a 1:1 mapping between each element in a TYPENAME and the EXP_FILTER list" );
1011 }
1012 }
1013
1014 if ( parameters.contains( QStringLiteral( "BBOX" ) ) )
1015 {
1016 // get bbox value
1017 QString bbox = parameters.value( QStringLiteral( "BBOX" ) );
1018 if ( bbox.isEmpty() )
1019 {
1020 throw QgsRequestNotWellFormedException( QStringLiteral( "BBOX parameter is empty" ) );
1021 }
1022
1023 // get bbox corners
1024 QStringList corners = bbox.split( ',' );
1025 if ( corners.size() != 4 )
1026 {
1027 throw QgsRequestNotWellFormedException( QStringLiteral( "BBOX has to be composed of 4 elements: '%1'" ).arg( bbox ) );
1028 }
1029
1030 // convert corners to double
1031 double d[4];
1032 bool ok;
1033 for ( int i = 0; i < 4; i++ )
1034 {
1035 corners[i].replace( ' ', '+' );
1036 d[i] = corners[i].toDouble( &ok );
1037 if ( !ok )
1038 {
1039 throw QgsRequestNotWellFormedException( QStringLiteral( "BBOX has to be composed of 4 double: '%1'" ).arg( bbox ) );
1040 }
1041 }
1042 // create extent
1043 QgsRectangle extent( d[0], d[1], d[2], d[3] );
1044
1045 // set feature request filter rectangle
1046 QList<transactionDelete>::iterator dIt = request.deletes.begin();
1047 for ( ; dIt != request.deletes.end(); ++dIt )
1048 {
1049 transactionDelete &action = *dIt;
1050 action.featureRequest.setFilterRect( extent );
1051 }
1052 return request;
1053 }
1054 else if ( parameters.contains( QStringLiteral( "FILTER" ) ) )
1055 {
1056 QString filterName = parameters.value( QStringLiteral( "FILTER" ) );
1057 QStringList filterList;
1058 const thread_local QRegularExpression rx( "\\(([^()]+)\\)" );
1059 QRegularExpressionMatchIterator matchIt = rx.globalMatch( filterName );
1060 if ( !matchIt.hasNext() )
1061 {
1062 filterList << filterName;
1063 }
1064 else
1065 {
1066 while ( matchIt.hasNext() )
1067 {
1068 const QRegularExpressionMatch match = matchIt.next();
1069 if ( match.hasMatch() )
1070 {
1071 QStringList matches = match.capturedTexts();
1072 matches.pop_front(); // remove whole match
1073 filterList.append( matches );
1074 }
1075 }
1076 }
1077
1078 // Verifying the 1:1 mapping between TYPENAME and FILTER
1079 if ( request.deletes.size() != filterList.size() )
1080 {
1081 throw QgsRequestNotWellFormedException( QStringLiteral( "There has to be a 1:1 mapping between each element in a TYPENAME and the FILTER list" ) );
1082 }
1083
1084 // set feature request filter expression based on filter element
1085 QList<transactionDelete>::iterator dIt = request.deletes.begin();
1086 QStringList::const_iterator filterIt = filterList.constBegin();
1087 for ( ; dIt != request.deletes.end(); ++dIt )
1088 {
1089 transactionDelete &action = *dIt;
1090
1091 // Get Filter for this typeName
1092 QDomDocument filter;
1093 if ( filterIt != filterList.constEnd() )
1094 {
1095 QString errorMsg;
1096 if ( !filter.setContent( *filterIt, true, &errorMsg ) )
1097 {
1098 throw QgsRequestNotWellFormedException( QStringLiteral( "error message: %1. The XML string was: %2" ).arg( errorMsg, *filterIt ) );
1099 }
1100 }
1101
1102 QDomElement filterElem = filter.firstChildElement();
1103 QStringList serverFids;
1104 action.featureRequest = parseFilterElement( action.typeName, filterElem, serverFids, project );
1105 action.serverFids = serverFids;
1106
1107 if ( filterIt != filterList.constEnd() )
1108 {
1109 ++filterIt;
1110 }
1111 }
1112 return request;
1113 }
1114
1115 return request;
1116 }
1117
1118 transactionRequest parseTransactionRequestBody( QDomElement &docElem, const QgsProject *project )
1119 {
1120 transactionRequest request;
1121
1122 QDomNodeList docChildNodes = docElem.childNodes();
1123
1124 QDomElement actionElem;
1125 QString actionName;
1126
1127 for ( int i = docChildNodes.count(); 0 < i; --i )
1128 {
1129 actionElem = docChildNodes.at( i - 1 ).toElement();
1130 actionName = actionElem.localName();
1131
1132 if ( actionName == QLatin1String( "Insert" ) )
1133 {
1134 transactionInsert action = parseInsertActionElement( actionElem );
1135 request.inserts.append( action );
1136 }
1137 else if ( actionName == QLatin1String( "Update" ) )
1138 {
1139 transactionUpdate action = parseUpdateActionElement( actionElem, project );
1140 request.updates.append( action );
1141 }
1142 else if ( actionName == QLatin1String( "Delete" ) )
1143 {
1144 transactionDelete action = parseDeleteActionElement( actionElem, project );
1145 request.deletes.append( action );
1146 }
1147 }
1148
1149 return request;
1150 }
1151
1152 transactionDelete parseDeleteActionElement( QDomElement &actionElem, const QgsProject *project )
1153 {
1154 QString typeName = actionElem.attribute( QStringLiteral( "typeName" ) );
1155 if ( typeName.contains( ':' ) )
1156 typeName = typeName.section( ':', 1, 1 );
1157
1158 QDomElement filterElem = actionElem.firstChild().toElement();
1159 if ( filterElem.tagName() != QLatin1String( "Filter" ) )
1160 {
1161 throw QgsRequestNotWellFormedException( QStringLiteral( "Delete action element first child is not Filter" ) );
1162 }
1163
1164 QStringList serverFids;
1165 QgsFeatureRequest featureRequest = parseFilterElement( typeName, filterElem, serverFids, project );
1166
1167 transactionDelete action;
1168 action.typeName = typeName;
1169 action.featureRequest = featureRequest;
1170 action.serverFids = serverFids;
1171 action.error = false;
1172
1173 if ( actionElem.hasAttribute( QStringLiteral( "handle" ) ) )
1174 {
1175 action.handle = actionElem.attribute( QStringLiteral( "handle" ) );
1176 }
1177
1178 return action;
1179 }
1180
1181 transactionUpdate parseUpdateActionElement( QDomElement &actionElem, const QgsProject *project )
1182 {
1183 QgsMessageLog::logMessage( QStringLiteral( "parseUpdateActionElement" ), QStringLiteral( "Server" ), Qgis::MessageLevel::Info );
1184 QString typeName = actionElem.attribute( QStringLiteral( "typeName" ) );
1185 if ( typeName.contains( ':' ) )
1186 typeName = typeName.section( ':', 1, 1 );
1187
1188 QDomNodeList propertyNodeList = actionElem.elementsByTagName( QStringLiteral( "Property" ) );
1189 if ( propertyNodeList.isEmpty() )
1190 {
1191 throw QgsRequestNotWellFormedException( QStringLiteral( "Update action element must have one or more Property element" ) );
1192 }
1193
1194 QMap<QString, QString> propertyMap;
1195 QDomElement propertyElem;
1196 QDomElement nameElem;
1197 QDomElement valueElem;
1198 QDomElement geometryElem;
1199
1200 for ( int l = 0; l < propertyNodeList.count(); ++l )
1201 {
1202 propertyElem = propertyNodeList.at( l ).toElement();
1203 nameElem = propertyElem.elementsByTagName( QStringLiteral( "Name" ) ).at( 0 ).toElement();
1204 valueElem = propertyElem.elementsByTagName( QStringLiteral( "Value" ) ).at( 0 ).toElement();
1205 if ( nameElem.text() != QLatin1String( "geometry" ) )
1206 {
1207 propertyMap.insert( nameElem.text(), valueElem.text() );
1208 }
1209 else
1210 {
1211 geometryElem = valueElem;
1212 }
1213 }
1214
1215 QDomNodeList filterNodeList = actionElem.elementsByTagName( QStringLiteral( "Filter" ) );
1216 QgsFeatureRequest featureRequest;
1217 QStringList serverFids;
1218 if ( filterNodeList.size() != 0 )
1219 {
1220 QDomElement filterElem = filterNodeList.at( 0 ).toElement();
1221 featureRequest = parseFilterElement( typeName, filterElem, serverFids, project );
1222 }
1223 QgsMessageLog::logMessage( QStringLiteral( "parseUpdateActionElement: serverFids length %1" ).arg( serverFids.count() ), QStringLiteral( "Server" ), Qgis::MessageLevel::Info );
1224
1225 transactionUpdate action;
1226 action.typeName = typeName;
1227 action.propertyMap = propertyMap;
1228 action.geometryElement = geometryElem;
1229 action.featureRequest = featureRequest;
1230 action.serverFids = serverFids;
1231 action.error = false;
1232
1233 if ( actionElem.hasAttribute( QStringLiteral( "handle" ) ) )
1234 {
1235 action.handle = actionElem.attribute( QStringLiteral( "handle" ) );
1236 }
1237
1238 return action;
1239 }
1240
1242 {
1243 QDomNodeList featureNodeList = actionElem.childNodes();
1244 if ( featureNodeList.size() != 1 )
1245 {
1246 throw QgsRequestNotWellFormedException( QStringLiteral( "Insert action element must have one or more child node" ) );
1247 }
1248
1249 QString typeName;
1250 for ( int i = 0; i < featureNodeList.count(); ++i )
1251 {
1252 QString tempTypeName = featureNodeList.at( i ).toElement().localName();
1253 if ( tempTypeName.contains( ':' ) )
1254 tempTypeName = tempTypeName.section( ':', 1, 1 );
1255
1256 if ( typeName.isEmpty() )
1257 {
1258 typeName = tempTypeName;
1259 }
1260 else if ( tempTypeName != typeName )
1261 {
1262 throw QgsRequestNotWellFormedException( QStringLiteral( "Insert action element must have one typename features" ) );
1263 }
1264 }
1265
1266 transactionInsert action;
1267 action.typeName = typeName;
1268 action.featureNodeList = featureNodeList;
1269 action.error = false;
1270
1271 if ( actionElem.hasAttribute( QStringLiteral( "handle" ) ) )
1272 {
1273 action.handle = actionElem.attribute( QStringLiteral( "handle" ) );
1274 }
1275
1276 return action;
1277 }
1278
1279 namespace
1280 {
1281
1282 void addTransactionResult( QDomDocument &responseDoc, QDomElement &resultsElem, const QString &locator, const QString &message )
1283 {
1284 QDomElement trElem = responseDoc.createElement( QStringLiteral( "Action" ) );
1285 resultsElem.appendChild( trElem );
1286
1287 if ( !locator.isEmpty() )
1288 {
1289 trElem.setAttribute( QStringLiteral( "locator" ), locator );
1290 }
1291
1292 if ( !message.isEmpty() )
1293 {
1294 QDomElement mesElem = responseDoc.createElement( QStringLiteral( "Message" ) );
1295 mesElem.appendChild( responseDoc.createTextNode( message ) );
1296 trElem.appendChild( mesElem );
1297 }
1298 }
1299
1300 } // namespace
1301
1302} // 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...
Class for parsing and evaluation of expressions (formerly called "search strings").
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
This class 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:76
QString id
Definition qgsmaplayer.h:79
Qgis::LayerType type
Definition qgsmaplayer.h:86
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).
RAII class to restore layer filters on destruction.
static void applyAccessControlLayerFilters(const QgsAccessControl *accessControl, QgsMapLayer *mapLayer, QHash< QgsMapLayer *, QString > &originalLayerFilters)
Apply filter from AccessControl.
Exception base class for service exceptions.
QString message() const
Returns the exception message.
static QgsGeometry geometryFromGML(const QString &xmlString, const QgsOgcUtils::Context &context=QgsOgcUtils::Context())
Static method that creates geometry from GML.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition qgsproject.h: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.
QgsServerInterface Class defining interfaces exposed by QGIS Server and made available to plugins.
virtual QgsAccessControl * accessControls() const =0
Gets the registered access control filters.
QgsServerRequest Class defining request interface passed to services QgsService::executeRequest() met...
QgsServerRequest::Parameters parameters() const
Returns a map of query parameters with keys converted to uppercase.
QMap< QString, QString > Parameters
virtual QByteArray data() const
Returns post/put data Check for QByteArray::isNull() to check if data is available.
QgsServerResponse Class defining response interface passed to services QgsService::executeRequest() m...
virtual void write(const QString &data)
Write string This is a convenient method that will write directly to the underlying I/O device.
virtual void setHeader(const QString &key, const QString &value)=0
Set Header entry Add Header entry to the response Note that it is usually an error to set Header afte...
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
This is the 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 data sets.
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.
SERVER_EXPORT QgsFeatureRequest updateFeatureRequestFromServerFids(QgsFeatureRequest &featureRequest, const QStringList &serverFids, const QgsVectorDataProvider *provider)
Returns the feature request based on feature ids build with primary keys.
SERVER_EXPORT QString getServerFid(const QgsFeature &feature, const QgsAttributeList &pkAttributes)
Returns the feature id based on primary keys.
SERVER_EXPORT QStringList wfsLayerIds(const QgsProject &project)
Returns the Layer ids list defined in a QGIS project as published in WFS.
SERVER_EXPORT QStringList wfstUpdateLayerIds(const QgsProject &project)
Returns the Layer ids list defined in a QGIS project as published as WFS-T with update capabilities.
SERVER_EXPORT QStringList wfstInsertLayerIds(const QgsProject &project)
Returns the Layer ids list defined in a QGIS project as published as WFS-T with insert capabilities.
SERVER_EXPORT QStringList wfstDeleteLayerIds(const QgsProject &project)
Returns the Layer ids list defined in a QGIS project as published as WFS-T with delete capabilities.
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.
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