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