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