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