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