QGIS API Documentation 3.29.0-Master (19d7edcfed)
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
40#include <QRegularExpression>
41
42namespace QgsWfs
43{
44 namespace v1_0_0
45 {
46 namespace
47 {
48 void addTransactionResult( QDomDocument &responseDoc, QDomElement &responseElem, const QString &status,
49 const QString &locator, const QString &message );
50 }
51
52
53 void writeTransaction( QgsServerInterface *serverIface, const QgsProject *project,
54 const QString &version, const QgsServerRequest &request,
55 QgsServerResponse &response )
56
57 {
58 QDomDocument doc = createTransactionDocument( serverIface, project, version, request );
59
60 response.setHeader( "Content-Type", "text/xml; charset=utf-8" );
61 response.write( doc.toByteArray() );
62 }
63
64 QDomDocument createTransactionDocument( QgsServerInterface *serverIface, const QgsProject *project,
65 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( QStringLiteral( "No actions found" ) );
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( QStringLiteral( "WFS_TransactionResponse" )/*wfs:WFS_TransactionResponse*/ );
98 respElem.setAttribute( QStringLiteral( "xmlns" ), WFS_NAMESPACE );
99 respElem.setAttribute( QStringLiteral( "xmlns:xsi" ), QStringLiteral( "http://www.w3.org/2001/XMLSchema-instance" ) );
100 respElem.setAttribute( QStringLiteral( "xsi:schemaLocation" ), WFS_NAMESPACE + " http://schemas.opengis.net/wfs/1.0.0/wfs.xsd" );
101 respElem.setAttribute( QStringLiteral( "xmlns:ogc" ), OGC_NAMESPACE );
102 respElem.setAttribute( QStringLiteral( "version" ), QStringLiteral( "1.0.0" ) );
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 << QStringLiteral( "Update:%1" ).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 << QStringLiteral( "Delete:%1" ).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 << QStringLiteral( "Insert:%1" ).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( QStringLiteral( "InsertResult" ) );
171 if ( !action.handle.isEmpty() )
172 {
173 irElem.setAttribute( QStringLiteral( "handle" ), action.handle );
174 }
175 QDomElement fiElem = doc.createElement( QStringLiteral( "ogc:FeatureId" ) );
176 fiElem.setAttribute( QStringLiteral( "fid" ), fidStr );
177 irElem.appendChild( fiElem );
178 respElem.appendChild( irElem );
179 }
180 }
181 }
182
183 // addTransactionResult
184 if ( errorCount == 0 )
185 {
186 addTransactionResult( resp, respElem, QStringLiteral( "SUCCESS" ), QString(), QString() );
187 }
188 else
189 {
190 QString locator = errorLocators.join( QLatin1String( "; " ) );
191 QString message = errorMessages.join( QLatin1String( "; " ) );
192 if ( errorCount != actionCount )
193 {
194 addTransactionResult( resp, respElem, QStringLiteral( "PARTIAL" ), locator, message );
195 }
196 else
197 {
198 addTransactionResult( resp, respElem, QStringLiteral( "ERROR" ), 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 std::unique_ptr< QgsOWSServerFilterRestorer > filterRestorer( new QgsOWSServerFilterRestorer() );
241
242 // get layers
243 QStringList wfsLayerIds = QgsServerProjectUtils::wfsLayerIds( *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() != QgsMapLayerType::VectorLayer )
252 {
253 continue;
254 }
255
256 QString name = layerTypeName( layer );
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( QStringLiteral( "Layer error on '%1'" ).arg( name ) );
268 }
269
270 //get provider
271 QgsVectorDataProvider *provider = vlayer->dataProvider();
272 if ( !provider )
273 {
274 throw QgsRequestNotWellFormedException( QStringLiteral( "Provider error on layer '%1'" ).arg( name ) );
275 }
276
277 // get provider capabilities
278 int cap = provider->capabilities();
281 {
282 throw QgsRequestNotWellFormedException( QStringLiteral( "No capabilities to do WFS changes on layer '%1'" ).arg( name ) );
283 }
284
285 if ( !wfstUpdateLayerIds.contains( vlayer->id() )
286 && !wfstDeleteLayerIds.contains( vlayer->id() )
287 && !wfstInsertLayerIds.contains( vlayer->id() ) )
288 {
289 throw QgsSecurityAccessException( QStringLiteral( "No permissions to do WFS changes on layer '%1'" ).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( QStringLiteral( "No permissions to do WFS changes on layer '%1'" ).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 = QStringLiteral( "TypeName '%1' unknown" ).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 = QStringLiteral( "No permissions to do WFS updates on layer '%1'" ).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 = QStringLiteral( "No permissions to do WFS updates on layer '%1'" ).arg( typeName );
336 continue;
337 }
338#endif
339 //get provider
340 QgsVectorDataProvider *provider = vlayer->dataProvider();
341
342 // verifying specific capabilities
343 int cap = provider->capabilities();
345 {
346 action.error = true;
347 action.errorMsg = QStringLiteral( "No capabilities to do WFS updates on layer '%1'" ).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 {
373 accessControl->filterFeatures( vlayer, featureRequest );
374 }
375#endif
376 // get iterator
377 QgsFeatureIterator fit = vlayer->getFeatures( featureRequest );
378 QgsFeature feature;
379 // get action properties
380 QMap<QString, QString> propertyMap = action.propertyMap;
381 QDomElement geometryElem = action.geometryElement;
382 // get field information
383 QgsFields fields = provider->fields();
384 const QMap<QString, int> fieldMap = provider->fieldNameMap();
385 QMap<QString, int>::const_iterator fieldMapIt;
386 QString fieldName;
387 bool conversionSuccess;
388 // Update the features
389 while ( fit.nextFeature( feature ) )
390 {
391#ifdef HAVE_SERVER_PYTHON_PLUGINS
392 if ( accessControl && !accessControl->allowToEdit( vlayer, feature ) )
393 {
394 action.error = true;
395 action.errorMsg = QStringLiteral( "Feature modify permission denied on layer '%1'" ).arg( typeName );
396 vlayer->rollBack();
397 break;
398 }
399#endif
400 QMap< QString, QString >::const_iterator it = propertyMap.constBegin();
401 for ( ; it != propertyMap.constEnd(); ++it )
402 {
403 fieldName = it.key();
404 fieldMapIt = fieldMap.find( fieldName );
405 if ( fieldMapIt == fieldMap.constEnd() )
406 {
407 continue;
408 }
409 QgsField field = fields.at( fieldMapIt.value() );
410 QVariant value = it.value();
411 if ( QgsVariantUtils::isNull( value ) )
412 {
413 if ( field.constraints().constraints() & QgsFieldConstraints::Constraint::ConstraintNotNull )
414 {
415 action.error = true;
416 action.errorMsg = QStringLiteral( "NOT NULL constraint error on layer '%1', field '%2'" ).arg( typeName, field.name() );
417 vlayer->rollBack();
418 break;
419 }
420 }
421 else // Not NULL
422 {
423 if ( field.type() == QVariant::Type::Int )
424 {
425 value = it.value().toInt( &conversionSuccess );
426 if ( !conversionSuccess )
427 {
428 action.error = true;
429 action.errorMsg = QStringLiteral( "Property conversion error on layer '%1'" ).arg( typeName );
430 vlayer->rollBack();
431 break;
432 }
433 }
434 else if ( field.type() == QVariant::Type::Double )
435 {
436 value = it.value().toDouble( &conversionSuccess );
437 if ( !conversionSuccess )
438 {
439 action.error = true;
440 action.errorMsg = QStringLiteral( "Property conversion error on layer '%1'" ).arg( typeName );
441 vlayer->rollBack();
442 break;
443 }
444 }
445 else if ( field.type() == QVariant::Type::LongLong )
446 {
447 value = it.value().toLongLong( &conversionSuccess );
448 if ( !conversionSuccess )
449 {
450 action.error = true;
451 action.errorMsg = QStringLiteral( "Property conversion error on layer '%1'" ).arg( typeName );
452 vlayer->rollBack();
453 break;
454 }
455 }
456 }
457 vlayer->changeAttributeValue( feature.id(), fieldMapIt.value(), value );
458 }
459 if ( action.error )
460 {
461 break;
462 }
463
464 if ( !geometryElem.isNull() )
465 {
466 const QgsOgcUtils::Context context { vlayer, provider->transformContext() };
467 QgsGeometry g = QgsOgcUtils::geometryFromGML( geometryElem, context );
468
469 if ( g.isNull() )
470 {
471 action.error = true;
472 action.errorMsg = QStringLiteral( "Geometry from GML error on layer '%1'" ).arg( typeName );
473 vlayer->rollBack();
474 break;
475 }
476 if ( !vlayer->changeGeometry( feature.id(), g ) )
477 {
478 action.error = true;
479 action.errorMsg = QStringLiteral( "Error in change geometry on layer '%1'" ).arg( typeName );
480 vlayer->rollBack();
481 break;
482 }
483 }
484 }
485 if ( action.error )
486 {
487 continue;
488 }
489#ifdef HAVE_SERVER_PYTHON_PLUGINS
490 // verifying changes
491 if ( accessControl )
492 {
493 fit = vlayer->getFeatures( featureRequest );
494 while ( fit.nextFeature( feature ) )
495 {
496 if ( accessControl && !accessControl->allowToEdit( vlayer, feature ) )
497 {
498 action.error = true;
499 action.errorMsg = QStringLiteral( "Feature modify permission denied on layer '%1'" ).arg( typeName );
500 vlayer->rollBack();
501 break;
502 }
503 }
504 }
505 if ( action.error )
506 {
507 continue;
508 }
509#endif
510
511 // Commit the changes of the update elements
512 if ( !vlayer->commitChanges() )
513 {
514 action.error = true;
515 action.errorMsg = QStringLiteral( "Error committing updates: %1" ).arg( vlayer->commitErrors().join( QLatin1String( "; " ) ) );
516 vlayer->rollBack();
517 continue;
518 }
519 // all the changes are OK!
520 action.error = false;
521
522 }
523
524 // perform deletes
525 tdIt = aRequest.deletes.begin();
526 for ( ; tdIt != aRequest.deletes.end(); ++tdIt )
527 {
528 transactionDelete &action = *tdIt;
529 QString typeName = action.typeName;
530
531 if ( !mapLayerMap.contains( typeName ) )
532 {
533 action.error = true;
534 action.errorMsg = QStringLiteral( "TypeName '%1' unknown" ).arg( typeName );
535 continue;
536 }
537
538 // get vector layer
539 QgsVectorLayer *vlayer = mapLayerMap[typeName];
540
541 // verifying specific permissions
542 if ( !wfstDeleteLayerIds.contains( vlayer->id() ) )
543 {
544 action.error = true;
545 action.errorMsg = QStringLiteral( "No permissions to do WFS deletes on layer '%1'" ).arg( typeName );
546 continue;
547 }
548#ifdef HAVE_SERVER_PYTHON_PLUGINS
549 if ( accessControl && !accessControl->layerDeletePermission( vlayer ) )
550 {
551 action.error = true;
552 action.errorMsg = QStringLiteral( "No permissions to do WFS deletes on layer '%1'" ).arg( typeName );
553 continue;
554 }
555#endif
556 //get provider
557 QgsVectorDataProvider *provider = vlayer->dataProvider();
558
559 // verifying specific capabilities
560 int cap = provider->capabilities();
562 {
563 action.error = true;
564 action.errorMsg = QStringLiteral( "No capabilities to do WFS deletes on layer '%1'" ).arg( typeName );
565 continue;
566 }
567 // start editing
568 vlayer->startEditing();
569
570 // delete request
571 QgsFeatureRequest featureRequest = action.featureRequest;
572
573 // expression context
574 QgsExpressionContext expressionContext;
575 expressionContext << QgsExpressionContextUtils::globalScope()
578 featureRequest.setExpressionContext( expressionContext );
579
580 // verifying feature ids list
581 if ( action.serverFids.isEmpty() )
582 {
583 action.error = true;
584 action.errorMsg = QStringLiteral( "No feature ids to do WFS deletes on layer '%1'" ).arg( typeName );
585 continue;
586 }
587
588 // update request based on feature ids
589 QgsServerFeatureId::updateFeatureRequestFromServerFids( featureRequest, action.serverFids, provider );
590
591#ifdef HAVE_SERVER_PYTHON_PLUGINS
592 if ( accessControl )
593 {
594 accessControl->filterFeatures( vlayer, featureRequest );
595 }
596#endif
597
598 // get iterator
599 QgsFeatureIterator fit = vlayer->getFeatures( featureRequest );
600 QgsFeature feature;
601 // get deleted fids
602 QgsFeatureIds fids;
603 while ( fit.nextFeature( feature ) )
604 {
605#ifdef HAVE_SERVER_PYTHON_PLUGINS
606 if ( accessControl && !accessControl->allowToEdit( vlayer, feature ) )
607 {
608 action.error = true;
609 action.errorMsg = QStringLiteral( "Feature modify permission denied" );
610 vlayer->rollBack();
611 break;
612 }
613#endif
614 fids << feature.id();
615 }
616 if ( action.error )
617 {
618 continue;
619 }
620 // delete features
621 if ( !vlayer->deleteFeatures( fids ) )
622 {
623 action.error = true;
624 action.errorMsg = QStringLiteral( "Delete features failed on layer '%1'" ).arg( typeName );
625 vlayer->rollBack();
626 continue;
627 }
628
629 // Commit the changes of the update elements
630 if ( !vlayer->commitChanges() )
631 {
632 action.error = true;
633 action.errorMsg = QStringLiteral( "Error committing deletes: %1" ).arg( vlayer->commitErrors().join( QLatin1String( "; " ) ) );
634 vlayer->rollBack();
635 continue;
636 }
637 // all the changes are OK!
638 action.error = false;
639 }
640
641 // perform inserts
642 tiIt = aRequest.inserts.begin();
643 for ( ; tiIt != aRequest.inserts.end(); ++tiIt )
644 {
645 transactionInsert &action = *tiIt;
646 QString typeName = action.typeName;
647
648 if ( !mapLayerMap.contains( typeName ) )
649 {
650 action.error = true;
651 action.errorMsg = QStringLiteral( "TypeName '%1' unknown" ).arg( typeName );
652 continue;
653 }
654
655 // get vector layer
656 QgsVectorLayer *vlayer = mapLayerMap[typeName];
657
658 // verifying specific permissions
659 if ( !wfstInsertLayerIds.contains( vlayer->id() ) )
660 {
661 action.error = true;
662 action.errorMsg = QStringLiteral( "No permissions to do WFS inserts on layer '%1'" ).arg( typeName );
663 continue;
664 }
665#ifdef HAVE_SERVER_PYTHON_PLUGINS
666 if ( accessControl && !accessControl->layerInsertPermission( vlayer ) )
667 {
668 action.error = true;
669 action.errorMsg = QStringLiteral( "No permissions to do WFS inserts on layer '%1'" ).arg( typeName );
670 continue;
671 }
672#endif
673 //get provider
674 QgsVectorDataProvider *provider = vlayer->dataProvider();
675
676 // verifying specific capabilities
677 int cap = provider->capabilities();
678 if ( !( cap & QgsVectorDataProvider::AddFeatures ) )
679 {
680 action.error = true;
681 action.errorMsg = QStringLiteral( "No capabilities to do WFS inserts on layer '%1'" ).arg( typeName );
682 continue;
683 }
684
685 // start editing
686 vlayer->startEditing();
687
688 // get inserting features
689 QgsFeatureList featureList;
690 try
691 {
692 featureList = featuresFromGML( action.featureNodeList, vlayer );
693 }
694 catch ( QgsOgcServiceException &ex )
695 {
696 action.error = true;
697 action.errorMsg = QStringLiteral( "%1 '%2'" ).arg( ex.message(), typeName );
698 continue;
699 }
700
701 if ( featureList.empty() )
702 {
703 action.error = true;
704 action.errorMsg = QStringLiteral( "No features to insert in layer '%1'" ).arg( typeName );
705 continue;
706 }
707
708#ifdef HAVE_SERVER_PYTHON_PLUGINS
709 // control features
710 if ( accessControl )
711 {
712 QgsFeatureList::iterator featureIt = featureList.begin();
713 while ( featureIt != featureList.end() )
714 {
715 if ( !accessControl->allowToEdit( vlayer, *featureIt ) )
716 {
717 action.error = true;
718 action.errorMsg = QStringLiteral( "Feature modify permission denied on layer '%1'" ).arg( typeName );
719 vlayer->rollBack();
720 break;
721 }
722 featureIt++;
723 }
724 }
725#endif
726 if ( action.error )
727 {
728 continue;
729 }
730
731 // perform add features
732 if ( !provider->addFeatures( featureList ) )
733 {
734 action.error = true;
735 action.errorMsg = QStringLiteral( "Insert features failed on layer '%1'" ).arg( typeName );
736 if ( provider ->hasErrors() )
737 {
738 provider->clearErrors();
739 }
740 vlayer->rollBack();
741 continue;
742 }
743
744 // Commit the changes of the update elements
745 if ( !vlayer->commitChanges() )
746 {
747 action.error = true;
748 action.errorMsg = QStringLiteral( "Error committing inserts: %1" ).arg( vlayer->commitErrors().join( QLatin1String( "; " ) ) );
749 vlayer->rollBack();
750 continue;
751 }
752 // all changes are OK!
753 action.error = false;
754
755 // Get the Feature Ids of the inserted feature
756 QgsAttributeList pkAttributes = provider->pkAttributeIndexes();
757 for ( const QgsFeature &feat : std::as_const( featureList ) )
758 {
759 action.insertFeatureIds << QStringLiteral( "%1.%2" ).arg( typeName, QgsServerFeatureId::getServerFid( feat, pkAttributes ) );
760 }
761 }
762
763 //force restoration of original layer filters
764 filterRestorer.reset();
765 }
766
767 QgsFeatureList featuresFromGML( QDomNodeList featureNodeList, QgsVectorLayer *layer )
768 {
769 // Store the inserted features
770 QgsFeatureList featList;
771
772 const auto provider { layer->dataProvider() };
773 Q_ASSERT( provider );
774
775 // Get Layer Field Information
776 QgsFields fields = provider->fields();
777 const QMap<QString, int> fieldMap = provider->fieldNameMap();
778 QMap<QString, int>::const_iterator fieldMapIt;
779
780 for ( int i = 0; i < featureNodeList.count(); i++ )
781 {
782 QgsFeature feat( fields );
783
784 QDomElement featureElem = featureNodeList.at( i ).toElement();
785 QDomNode currentAttributeChild = featureElem.firstChild();
786 bool conversionSuccess = true;
787
788 while ( !currentAttributeChild.isNull() )
789 {
790 QDomElement currentAttributeElement = currentAttributeChild.toElement();
791 QString attrName = currentAttributeElement.localName();
792
793 if ( attrName != QLatin1String( "boundedBy" ) )
794 {
795 if ( attrName != QLatin1String( "geometry" ) ) //a normal attribute
796 {
797 fieldMapIt = fieldMap.find( attrName );
798 if ( fieldMapIt == fieldMap.constEnd() )
799 {
800 continue;
801 }
802
803 QgsField field = fields.at( fieldMapIt.value() );
804 QString attrValue = currentAttributeElement.text();
805 int attrType = field.type();
806
807 QgsMessageLog::logMessage( QStringLiteral( "attr: name=%1 idx=%2 value=%3" ).arg( attrName ).arg( fieldMapIt.value() ).arg( attrValue ) );
808
809 if ( attrType == QVariant::Int )
810 feat.setAttribute( fieldMapIt.value(), attrValue.toInt( &conversionSuccess ) );
811 else if ( attrType == QVariant::Double )
812 feat.setAttribute( fieldMapIt.value(), attrValue.toDouble( &conversionSuccess ) );
813 else
814 feat.setAttribute( fieldMapIt.value(), attrValue );
815
816 if ( !conversionSuccess )
817 {
818 throw QgsRequestNotWellFormedException( QStringLiteral( "Property conversion error on layer insert" ) );
819 }
820 }
821 else //a geometry attribute
822 {
823 const QgsOgcUtils::Context context { layer, provider->transformContext() };
824 QgsGeometry g = QgsOgcUtils::geometryFromGML( currentAttributeElement, context );
825 if ( g.isNull() )
826 {
827 throw QgsRequestNotWellFormedException( QStringLiteral( "Geometry from GML error on layer insert" ) );
828 }
829 feat.setGeometry( g );
830 }
831 }
832 currentAttributeChild = currentAttributeChild.nextSibling();
833 }
834 // update feature list
835 featList << feat;
836 }
837 return featList;
838 }
839
841 {
842 if ( !parameters.contains( QStringLiteral( "OPERATION" ) ) )
843 {
844 throw QgsRequestNotWellFormedException( QStringLiteral( "OPERATION parameter is mandatory" ) );
845 }
846 if ( parameters.value( QStringLiteral( "OPERATION" ) ).toUpper() != QLatin1String( "DELETE" ) )
847 {
848 throw QgsRequestNotWellFormedException( QStringLiteral( "Only DELETE value is defined for OPERATION parameter" ) );
849 }
850
851 // Verifying parameters mutually exclusive
852 if ( ( parameters.contains( QStringLiteral( "FEATUREID" ) )
853 && ( parameters.contains( QStringLiteral( "FILTER" ) ) || parameters.contains( QStringLiteral( "BBOX" ) ) ) )
854 || ( parameters.contains( QStringLiteral( "FILTER" ) )
855 && ( parameters.contains( QStringLiteral( "FEATUREID" ) ) || parameters.contains( QStringLiteral( "BBOX" ) ) ) )
856 || ( parameters.contains( QStringLiteral( "BBOX" ) )
857 && ( parameters.contains( QStringLiteral( "FEATUREID" ) ) || parameters.contains( QStringLiteral( "FILTER" ) ) ) )
858 )
859 {
860 throw QgsRequestNotWellFormedException( QStringLiteral( "FEATUREID FILTER and BBOX parameters are mutually exclusive" ) );
861 }
862
863 transactionRequest request;
864
865 QStringList typeNameList;
866 // parse FEATUREID
867 if ( parameters.contains( QStringLiteral( "FEATUREID" ) ) )
868 {
869 QStringList fidList = parameters.value( QStringLiteral( "FEATUREID" ) ).split( ',' );
870
871 QMap<QString, QStringList> fidsMap;
872
873 QStringList::const_iterator fidIt = fidList.constBegin();
874 for ( ; fidIt != fidList.constEnd(); ++fidIt )
875 {
876 // Get FeatureID
877 QString fid = *fidIt;
878 fid = fid.trimmed();
879 // testing typename in the WFS featureID
880 if ( !fid.contains( '.' ) )
881 {
882 throw QgsRequestNotWellFormedException( QStringLiteral( "FEATUREID has to have TYPENAME in the values" ) );
883 }
884
885 QString typeName = fid.section( '.', 0, 0 );
886 fid = fid.section( '.', 1, 1 );
887 if ( !typeNameList.contains( typeName ) )
888 {
889 typeNameList << typeName;
890 }
891
892 QStringList fids;
893 if ( fidsMap.contains( typeName ) )
894 {
895 fids = fidsMap.value( typeName );
896 }
897 fids.append( fid );
898 fidsMap.insert( typeName, fids );
899 }
900
901 QMap<QString, QStringList>::const_iterator fidsMapIt = fidsMap.constBegin();
902 while ( fidsMapIt != fidsMap.constEnd() )
903 {
904 transactionDelete action;
905 action.typeName = fidsMapIt.key();
906
907 action.serverFids = fidsMapIt.value();
909
910 request.deletes.append( action );
911 }
912 return request;
913 }
914
915 if ( !parameters.contains( QStringLiteral( "TYPENAME" ) ) )
916 {
917 throw QgsRequestNotWellFormedException( QStringLiteral( "TYPENAME is mandatory except if FEATUREID is used" ) );
918 }
919
920 typeNameList = parameters.value( QStringLiteral( "TYPENAME" ) ).split( ',' );
921
922 // Create actions based on TypeName
923 QStringList::const_iterator typeNameIt = typeNameList.constBegin();
924 for ( ; typeNameIt != typeNameList.constEnd(); ++typeNameIt )
925 {
926 QString typeName = *typeNameIt;
927 typeName = typeName.trimmed();
928
929 transactionDelete action;
930 action.typeName = typeName;
931
932 request.deletes.append( action );
933 }
934
935 // Manage extra parameter exp_filter
936 if ( parameters.contains( QStringLiteral( "EXP_FILTER" ) ) )
937 {
938 QString expFilterName = parameters.value( QStringLiteral( "EXP_FILTER" ) );
939 QStringList expFilterList;
940 const thread_local QRegularExpression rx( "\\(([^()]+)\\)" );
941 QRegularExpressionMatchIterator matchIt = rx.globalMatch( expFilterName );
942 if ( !matchIt.hasNext() )
943 {
944 expFilterList << expFilterName;
945 }
946 else
947 {
948 while ( matchIt.hasNext() )
949 {
950 const QRegularExpressionMatch match = matchIt.next();
951 if ( match.hasMatch() )
952 {
953 QStringList matches = match.capturedTexts();
954 matches.pop_front(); // remove whole match
955 expFilterList.append( matches );
956 }
957 }
958 }
959
960 // Verifying the 1:1 mapping between TYPENAME and EXP_FILTER but without exception
961 if ( request.deletes.size() == expFilterList.size() )
962 {
963 // set feature request filter expression based on filter element
964 QList<transactionDelete>::iterator dIt = request.deletes.begin();
965 QStringList::const_iterator expFilterIt = expFilterList.constBegin();
966 for ( ; dIt != request.deletes.end(); ++dIt )
967 {
968 transactionDelete &action = *dIt;
969 // Get Filter for this typeName
970 QString expFilter;
971 if ( expFilterIt != expFilterList.constEnd() )
972 {
973 expFilter = *expFilterIt;
974 }
975 std::shared_ptr<QgsExpression> filter( new QgsExpression( expFilter ) );
976 if ( filter )
977 {
978 if ( filter->hasParserError() )
979 {
980 QgsMessageLog::logMessage( filter->parserErrorString() );
981 }
982 else
983 {
984 if ( filter->needsGeometry() )
985 {
987 }
988 action.featureRequest.setFilterExpression( filter->expression() );
989 }
990 }
991 }
992 }
993 else
994 {
995 QgsMessageLog::logMessage( "There has to be a 1:1 mapping between each element in a TYPENAME and the EXP_FILTER list" );
996 }
997 }
998
999 if ( parameters.contains( QStringLiteral( "BBOX" ) ) )
1000 {
1001 // get bbox value
1002 QString bbox = parameters.value( QStringLiteral( "BBOX" ) );
1003 if ( bbox.isEmpty() )
1004 {
1005 throw QgsRequestNotWellFormedException( QStringLiteral( "BBOX parameter is empty" ) );
1006 }
1007
1008 // get bbox corners
1009 QStringList corners = bbox.split( ',' );
1010 if ( corners.size() != 4 )
1011 {
1012 throw QgsRequestNotWellFormedException( QStringLiteral( "BBOX has to be composed of 4 elements: '%1'" ).arg( bbox ) );
1013 }
1014
1015 // convert corners to double
1016 double d[4];
1017 bool ok;
1018 for ( int i = 0; i < 4; i++ )
1019 {
1020 corners[i].replace( ' ', '+' );
1021 d[i] = corners[i].toDouble( &ok );
1022 if ( !ok )
1023 {
1024 throw QgsRequestNotWellFormedException( QStringLiteral( "BBOX has to be composed of 4 double: '%1'" ).arg( bbox ) );
1025 }
1026 }
1027 // create extent
1028 QgsRectangle extent( d[0], d[1], d[2], d[3] );
1029
1030 // set feature request filter rectangle
1031 QList<transactionDelete>::iterator dIt = request.deletes.begin();
1032 for ( ; dIt != request.deletes.end(); ++dIt )
1033 {
1034 transactionDelete &action = *dIt;
1035 action.featureRequest.setFilterRect( extent );
1036 }
1037 return request;
1038 }
1039 else if ( parameters.contains( QStringLiteral( "FILTER" ) ) )
1040 {
1041 QString filterName = parameters.value( QStringLiteral( "FILTER" ) );
1042 QStringList filterList;
1043
1044 const thread_local QRegularExpression rx( "\\(([^()]+)\\)" );
1045 QRegularExpressionMatchIterator matchIt = rx.globalMatch( filterName );
1046 if ( !matchIt.hasNext() )
1047 {
1048 filterList << filterName;
1049 }
1050 else
1051 {
1052 while ( matchIt.hasNext() )
1053 {
1054 const QRegularExpressionMatch match = matchIt.next();
1055 if ( match.hasMatch() )
1056 {
1057 QStringList matches = match.capturedTexts();
1058 matches.pop_front(); // remove whole match
1059 filterList.append( matches );
1060 }
1061 }
1062 }
1063
1064 // Verifying the 1:1 mapping between TYPENAME and FILTER
1065 if ( request.deletes.size() != filterList.size() )
1066 {
1067 throw QgsRequestNotWellFormedException( QStringLiteral( "There has to be a 1:1 mapping between each element in a TYPENAME and the FILTER list" ) );
1068 }
1069
1070 // set feature request filter expression based on filter element
1071 QList<transactionDelete>::iterator dIt = request.deletes.begin();
1072 QStringList::const_iterator filterIt = filterList.constBegin();
1073 for ( ; dIt != request.deletes.end(); ++dIt )
1074 {
1075 transactionDelete &action = *dIt;
1076
1077 // Get Filter for this typeName
1078 QDomDocument filter;
1079 if ( filterIt != filterList.constEnd() )
1080 {
1081 QString errorMsg;
1082 if ( !filter.setContent( *filterIt, true, &errorMsg ) )
1083 {
1084 throw QgsRequestNotWellFormedException( QStringLiteral( "error message: %1. The XML string was: %2" ).arg( errorMsg, *filterIt ) );
1085 }
1086 }
1087
1088 QDomElement filterElem = filter.firstChildElement();
1089 QStringList serverFids;
1090 action.featureRequest = parseFilterElement( action.typeName, filterElem, serverFids, project );
1091 action.serverFids = serverFids;
1092
1093 if ( filterIt != filterList.constEnd() )
1094 {
1095 ++filterIt;
1096 }
1097 }
1098 return request;
1099 }
1100
1101 return request;
1102 }
1103
1104 transactionRequest parseTransactionRequestBody( QDomElement &docElem, const QgsProject *project )
1105 {
1106 transactionRequest request;
1107
1108 QDomNodeList docChildNodes = docElem.childNodes();
1109
1110 QDomElement actionElem;
1111 QString actionName;
1112
1113 for ( int i = docChildNodes.count(); 0 < i; --i )
1114 {
1115 actionElem = docChildNodes.at( i - 1 ).toElement();
1116 actionName = actionElem.localName();
1117
1118 if ( actionName == QLatin1String( "Insert" ) )
1119 {
1120 transactionInsert action = parseInsertActionElement( actionElem );
1121 request.inserts.append( action );
1122 }
1123 else if ( actionName == QLatin1String( "Update" ) )
1124 {
1125 transactionUpdate action = parseUpdateActionElement( actionElem, project );
1126 request.updates.append( action );
1127 }
1128 else if ( actionName == QLatin1String( "Delete" ) )
1129 {
1130 transactionDelete action = parseDeleteActionElement( actionElem, project );
1131 request.deletes.append( action );
1132 }
1133 }
1134
1135 return request;
1136 }
1137
1138 transactionDelete parseDeleteActionElement( QDomElement &actionElem, const QgsProject *project )
1139 {
1140 QString typeName = actionElem.attribute( QStringLiteral( "typeName" ) );
1141 if ( typeName.contains( ':' ) )
1142 typeName = typeName.section( ':', 1, 1 );
1143
1144 QDomElement filterElem = actionElem.firstChild().toElement();
1145 if ( filterElem.tagName() != QLatin1String( "Filter" ) )
1146 {
1147 throw QgsRequestNotWellFormedException( QStringLiteral( "Delete action element first child is not Filter" ) );
1148 }
1149
1150 QStringList serverFids;
1151 QgsFeatureRequest featureRequest = parseFilterElement( typeName, filterElem, serverFids, project );
1152
1153 transactionDelete action;
1154 action.typeName = typeName;
1155 action.featureRequest = featureRequest;
1156 action.serverFids = serverFids;
1157 action.error = false;
1158
1159 if ( actionElem.hasAttribute( QStringLiteral( "handle" ) ) )
1160 {
1161 action.handle = actionElem.attribute( QStringLiteral( "handle" ) );
1162 }
1163
1164 return action;
1165 }
1166
1167 transactionUpdate parseUpdateActionElement( QDomElement &actionElem, const QgsProject *project )
1168 {
1169 QString typeName = actionElem.attribute( QStringLiteral( "typeName" ) );
1170 if ( typeName.contains( ':' ) )
1171 typeName = typeName.section( ':', 1, 1 );
1172
1173 QDomNodeList propertyNodeList = actionElem.elementsByTagName( QStringLiteral( "Property" ) );
1174 if ( propertyNodeList.isEmpty() )
1175 {
1176 throw QgsRequestNotWellFormedException( QStringLiteral( "Update action element must have one or more Property element" ) );
1177 }
1178
1179 QMap<QString, QString> propertyMap;
1180 QDomElement propertyElem;
1181 QDomElement nameElem;
1182 QDomElement valueElem;
1183 QDomElement geometryElem;
1184
1185 for ( int l = 0; l < propertyNodeList.count(); ++l )
1186 {
1187 propertyElem = propertyNodeList.at( l ).toElement();
1188 nameElem = propertyElem.elementsByTagName( QStringLiteral( "Name" ) ).at( 0 ).toElement();
1189 valueElem = propertyElem.elementsByTagName( QStringLiteral( "Value" ) ).at( 0 ).toElement();
1190 if ( nameElem.text() != QLatin1String( "geometry" ) )
1191 {
1192 propertyMap.insert( nameElem.text(), valueElem.text() );
1193 }
1194 else
1195 {
1196 geometryElem = valueElem;
1197 }
1198 }
1199
1200 QDomNodeList filterNodeList = actionElem.elementsByTagName( QStringLiteral( "Filter" ) );
1201 QgsFeatureRequest featureRequest;
1202 QStringList serverFids;
1203 if ( filterNodeList.size() != 0 )
1204 {
1205 QDomElement filterElem = filterNodeList.at( 0 ).toElement();
1206 featureRequest = parseFilterElement( typeName, filterElem, serverFids, project );
1207 }
1208
1209 transactionUpdate action;
1210 action.typeName = typeName;
1211 action.propertyMap = propertyMap;
1212 action.geometryElement = geometryElem;
1213 action.featureRequest = featureRequest;
1214 action.serverFids = serverFids;
1215 action.error = false;
1216
1217 if ( actionElem.hasAttribute( QStringLiteral( "handle" ) ) )
1218 {
1219 action.handle = actionElem.attribute( QStringLiteral( "handle" ) );
1220 }
1221
1222 return action;
1223 }
1224
1226 {
1227 QDomNodeList featureNodeList = actionElem.childNodes();
1228 if ( featureNodeList.size() != 1 )
1229 {
1230 throw QgsRequestNotWellFormedException( QStringLiteral( "Insert action element must have one or more child node" ) );
1231 }
1232
1233 QString typeName;
1234 for ( int i = 0; i < featureNodeList.count(); ++i )
1235 {
1236 QString tempTypeName = featureNodeList.at( i ).toElement().localName();
1237 if ( tempTypeName.contains( ':' ) )
1238 tempTypeName = tempTypeName.section( ':', 1, 1 );
1239
1240 if ( typeName.isEmpty() )
1241 {
1242 typeName = tempTypeName;
1243 }
1244 else if ( tempTypeName != typeName )
1245 {
1246 throw QgsRequestNotWellFormedException( QStringLiteral( "Insert action element must have one typename features" ) );
1247 }
1248 }
1249
1250 transactionInsert action;
1251 action.typeName = typeName;
1252 action.featureNodeList = featureNodeList;
1253 action.error = false;
1254
1255 if ( actionElem.hasAttribute( QStringLiteral( "handle" ) ) )
1256 {
1257 action.handle = actionElem.attribute( QStringLiteral( "handle" ) );
1258 }
1259
1260 return action;
1261 }
1262
1263 namespace
1264 {
1265
1266 void addTransactionResult( QDomDocument &responseDoc, QDomElement &responseElem, const QString &status,
1267 const QString &locator, const QString &message )
1268 {
1269 QDomElement trElem = responseDoc.createElement( QStringLiteral( "TransactionResult" ) );
1270 QDomElement stElem = responseDoc.createElement( QStringLiteral( "Status" ) );
1271 QDomElement successElem = responseDoc.createElement( status );
1272 stElem.appendChild( successElem );
1273 trElem.appendChild( stElem );
1274 responseElem.appendChild( trElem );
1275
1276 if ( !locator.isEmpty() )
1277 {
1278 QDomElement locElem = responseDoc.createElement( QStringLiteral( "Locator" ) );
1279 locElem.appendChild( responseDoc.createTextNode( locator ) );
1280 trElem.appendChild( locElem );
1281 }
1282
1283 if ( !message.isEmpty() )
1284 {
1285 QDomElement mesElem = responseDoc.createElement( QStringLiteral( "Message" ) );
1286 mesElem.appendChild( responseDoc.createTextNode( message ) );
1287 trElem.appendChild( mesElem );
1288 }
1289 }
1290
1291 }
1292
1293 } // namespace v1_0_0
1294} // namespace QgsWfs
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:265
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:164
Q_GADGET bool isNull
Definition: qgsgeometry.h:166
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:105
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...
static bool isNull(const QVariant &variant)
Returns true if the specified variant should be considered a NULL value.
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:920
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