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