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