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