QGIS API Documentation 3.41.0-Master (af5edcb665c)
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
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 std::unique_ptr<QgsOWSServerFilterRestorer> filterRestorer( new 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 {
369 accessControl->filterFeatures( vlayer, featureRequest );
370 }
371#endif
372 // get iterator
373 QgsFeatureIterator fit = vlayer->getFeatures( featureRequest );
374 QgsFeature feature;
375 // get action properties
376 QMap<QString, QString> propertyMap = action.propertyMap;
377 QDomElement geometryElem = action.geometryElement;
378 // get field information
379 QgsFields fields = provider->fields();
380 const QMap<QString, int> fieldMap = provider->fieldNameMap();
381 QMap<QString, int>::const_iterator fieldMapIt;
382 QString fieldName;
383 bool conversionSuccess;
384 // Update the features
385 while ( fit.nextFeature( feature ) )
386 {
387#ifdef HAVE_SERVER_PYTHON_PLUGINS
388 if ( accessControl && !accessControl->allowToEdit( vlayer, feature ) )
389 {
390 action.error = true;
391 action.errorMsg = QStringLiteral( "Feature modify permission denied on layer '%1'" ).arg( typeName );
392 vlayer->rollBack();
393 break;
394 }
395#endif
396 QMap<QString, QString>::const_iterator it = propertyMap.constBegin();
397 for ( ; it != propertyMap.constEnd(); ++it )
398 {
399 fieldName = it.key();
400 fieldMapIt = fieldMap.find( fieldName );
401 if ( fieldMapIt == fieldMap.constEnd() )
402 {
403 continue;
404 }
405 QgsField field = fields.at( fieldMapIt.value() );
406 QVariant value = it.value();
407 if ( QgsVariantUtils::isNull( value ) )
408 {
410 {
411 action.error = true;
412 action.errorMsg = QStringLiteral( "NOT NULL constraint error on layer '%1', field '%2'" ).arg( typeName, field.name() );
413 vlayer->rollBack();
414 break;
415 }
416 }
417 else // Not NULL
418 {
419 if ( field.type() == QMetaType::Type::Int )
420 {
421 value = it.value().toInt( &conversionSuccess );
422 if ( !conversionSuccess )
423 {
424 action.error = true;
425 action.errorMsg = QStringLiteral( "Property conversion error on layer '%1'" ).arg( typeName );
426 vlayer->rollBack();
427 break;
428 }
429 }
430 else if ( field.type() == QMetaType::Type::Double )
431 {
432 value = it.value().toDouble( &conversionSuccess );
433 if ( !conversionSuccess )
434 {
435 action.error = true;
436 action.errorMsg = QStringLiteral( "Property conversion error on layer '%1'" ).arg( typeName );
437 vlayer->rollBack();
438 break;
439 }
440 }
441 else if ( field.type() == QMetaType::Type::LongLong )
442 {
443 value = it.value().toLongLong( &conversionSuccess );
444 if ( !conversionSuccess )
445 {
446 action.error = true;
447 action.errorMsg = QStringLiteral( "Property conversion error on layer '%1'" ).arg( typeName );
448 vlayer->rollBack();
449 break;
450 }
451 }
452 }
453 vlayer->changeAttributeValue( feature.id(), fieldMapIt.value(), value );
454 }
455 if ( action.error )
456 {
457 break;
458 }
459
460 if ( !geometryElem.isNull() )
461 {
462 const QgsOgcUtils::Context context { vlayer, provider->transformContext() };
463 QgsGeometry g = QgsOgcUtils::geometryFromGML( geometryElem, context );
464
465 if ( g.isNull() )
466 {
467 action.error = true;
468 action.errorMsg = QStringLiteral( "Geometry from GML error on layer '%1'" ).arg( typeName );
469 vlayer->rollBack();
470 break;
471 }
472 if ( !vlayer->changeGeometry( feature.id(), g ) )
473 {
474 action.error = true;
475 action.errorMsg = QStringLiteral( "Error in change geometry on layer '%1'" ).arg( typeName );
476 vlayer->rollBack();
477 break;
478 }
479 }
480 }
481 if ( action.error )
482 {
483 continue;
484 }
485#ifdef HAVE_SERVER_PYTHON_PLUGINS
486 // verifying changes
487 if ( accessControl )
488 {
489 fit = vlayer->getFeatures( featureRequest );
490 while ( fit.nextFeature( feature ) )
491 {
492 if ( accessControl && !accessControl->allowToEdit( vlayer, feature ) )
493 {
494 action.error = true;
495 action.errorMsg = QStringLiteral( "Feature modify permission denied on layer '%1'" ).arg( typeName );
496 vlayer->rollBack();
497 break;
498 }
499 }
500 }
501 if ( action.error )
502 {
503 continue;
504 }
505#endif
506
507 // Commit the changes of the update elements
508 if ( !vlayer->commitChanges() )
509 {
510 action.error = true;
511 action.errorMsg = QStringLiteral( "Error committing updates: %1" ).arg( vlayer->commitErrors().join( QLatin1String( "; " ) ) );
512 vlayer->rollBack();
513 continue;
514 }
515 // all the changes are OK!
516 action.error = false;
517 }
518
519 // perform deletes
520 tdIt = aRequest.deletes.begin();
521 for ( ; tdIt != aRequest.deletes.end(); ++tdIt )
522 {
523 transactionDelete &action = *tdIt;
524 QString typeName = action.typeName;
525
526 if ( !mapLayerMap.contains( typeName ) )
527 {
528 action.error = true;
529 action.errorMsg = QStringLiteral( "TypeName '%1' unknown" ).arg( typeName );
530 continue;
531 }
532
533 // get vector layer
534 QgsVectorLayer *vlayer = mapLayerMap[typeName];
535
536 // verifying specific permissions
537 if ( !wfstDeleteLayerIds.contains( vlayer->id() ) )
538 {
539 action.error = true;
540 action.errorMsg = QStringLiteral( "No permissions to do WFS deletes on layer '%1'" ).arg( typeName );
541 continue;
542 }
543#ifdef HAVE_SERVER_PYTHON_PLUGINS
544 if ( accessControl && !accessControl->layerDeletePermission( vlayer ) )
545 {
546 action.error = true;
547 action.errorMsg = QStringLiteral( "No permissions to do WFS deletes on layer '%1'" ).arg( typeName );
548 continue;
549 }
550#endif
551 //get provider
552 QgsVectorDataProvider *provider = vlayer->dataProvider();
553
554 // verifying specific capabilities
557 {
558 action.error = true;
559 action.errorMsg = QStringLiteral( "No capabilities to do WFS deletes on layer '%1'" ).arg( typeName );
560 continue;
561 }
562 // start editing
563 vlayer->startEditing();
564
565 // delete request
566 QgsFeatureRequest featureRequest = action.featureRequest;
567
568 // expression context
569 QgsExpressionContext expressionContext;
570 expressionContext << QgsExpressionContextUtils::globalScope()
573 featureRequest.setExpressionContext( expressionContext );
574
575 // verifying feature ids list
576 if ( action.serverFids.isEmpty() )
577 {
578 action.error = true;
579 action.errorMsg = QStringLiteral( "No feature ids to do WFS deletes on layer '%1'" ).arg( typeName );
580 continue;
581 }
582
583 // update request based on feature ids
584 QgsServerFeatureId::updateFeatureRequestFromServerFids( featureRequest, action.serverFids, provider );
585
586#ifdef HAVE_SERVER_PYTHON_PLUGINS
587 if ( accessControl )
588 {
589 accessControl->filterFeatures( vlayer, featureRequest );
590 }
591#endif
592
593 // get iterator
594 QgsFeatureIterator fit = vlayer->getFeatures( featureRequest );
595 QgsFeature feature;
596 // get deleted fids
597 QgsFeatureIds fids;
598 while ( fit.nextFeature( feature ) )
599 {
600#ifdef HAVE_SERVER_PYTHON_PLUGINS
601 if ( accessControl && !accessControl->allowToEdit( vlayer, feature ) )
602 {
603 action.error = true;
604 action.errorMsg = QStringLiteral( "Feature modify permission denied" );
605 vlayer->rollBack();
606 break;
607 }
608#endif
609 fids << feature.id();
610 }
611 if ( action.error )
612 {
613 continue;
614 }
615 // delete features
616 if ( !vlayer->deleteFeatures( fids ) )
617 {
618 action.error = true;
619 action.errorMsg = QStringLiteral( "Delete features failed on layer '%1'" ).arg( typeName );
620 vlayer->rollBack();
621 continue;
622 }
623
624 // Commit the changes of the update elements
625 if ( !vlayer->commitChanges() )
626 {
627 action.error = true;
628 action.errorMsg = QStringLiteral( "Error committing deletes: %1" ).arg( vlayer->commitErrors().join( QLatin1String( "; " ) ) );
629 vlayer->rollBack();
630 continue;
631 }
632 // all the changes are OK!
633 action.error = false;
634 }
635
636 // perform inserts
637 tiIt = aRequest.inserts.begin();
638 for ( ; tiIt != aRequest.inserts.end(); ++tiIt )
639 {
640 transactionInsert &action = *tiIt;
641 QString typeName = action.typeName;
642
643 if ( !mapLayerMap.contains( typeName ) )
644 {
645 action.error = true;
646 action.errorMsg = QStringLiteral( "TypeName '%1' unknown" ).arg( typeName );
647 continue;
648 }
649
650 // get vector layer
651 QgsVectorLayer *vlayer = mapLayerMap[typeName];
652
653 // verifying specific permissions
654 if ( !wfstInsertLayerIds.contains( vlayer->id() ) )
655 {
656 action.error = true;
657 action.errorMsg = QStringLiteral( "No permissions to do WFS inserts on layer '%1'" ).arg( typeName );
658 continue;
659 }
660#ifdef HAVE_SERVER_PYTHON_PLUGINS
661 if ( accessControl && !accessControl->layerInsertPermission( vlayer ) )
662 {
663 action.error = true;
664 action.errorMsg = QStringLiteral( "No permissions to do WFS inserts on layer '%1'" ).arg( typeName );
665 continue;
666 }
667#endif
668 //get provider
669 QgsVectorDataProvider *provider = vlayer->dataProvider();
670
671 // verifying specific capabilities
674 {
675 action.error = true;
676 action.errorMsg = QStringLiteral( "No capabilities to do WFS inserts on layer '%1'" ).arg( typeName );
677 continue;
678 }
679
680 // start editing
681 vlayer->startEditing();
682
683 // get inserting features
684 QgsFeatureList featureList;
685 try
686 {
687 featureList = featuresFromGML( action.featureNodeList, vlayer );
688 }
689 catch ( QgsOgcServiceException &ex )
690 {
691 action.error = true;
692 action.errorMsg = QStringLiteral( "%1 '%2'" ).arg( ex.message(), typeName );
693 continue;
694 }
695
696 if ( featureList.empty() )
697 {
698 action.error = true;
699 action.errorMsg = QStringLiteral( "No features to insert in layer '%1'" ).arg( typeName );
700 continue;
701 }
702
703#ifdef HAVE_SERVER_PYTHON_PLUGINS
704 // control features
705 if ( accessControl )
706 {
707 QgsFeatureList::iterator featureIt = featureList.begin();
708 while ( featureIt != featureList.end() )
709 {
710 if ( !accessControl->allowToEdit( vlayer, *featureIt ) )
711 {
712 action.error = true;
713 action.errorMsg = QStringLiteral( "Feature modify permission denied on layer '%1'" ).arg( typeName );
714 vlayer->rollBack();
715 break;
716 }
717 featureIt++;
718 }
719 }
720#endif
721 if ( action.error )
722 {
723 continue;
724 }
725
726 // perform add features
727 if ( !provider->addFeatures( featureList ) )
728 {
729 action.error = true;
730 action.errorMsg = QStringLiteral( "Insert features failed on layer '%1'" ).arg( typeName );
731 if ( provider->hasErrors() )
732 {
733 provider->clearErrors();
734 }
735 vlayer->rollBack();
736 continue;
737 }
738
739 // Commit the changes of the update elements
740 if ( !vlayer->commitChanges() )
741 {
742 action.error = true;
743 action.errorMsg = QStringLiteral( "Error committing inserts: %1" ).arg( vlayer->commitErrors().join( QLatin1String( "; " ) ) );
744 vlayer->rollBack();
745 continue;
746 }
747 // all changes are OK!
748 action.error = false;
749
750 // Get the Feature Ids of the inserted feature
751 QgsAttributeList pkAttributes = provider->pkAttributeIndexes();
752 for ( const QgsFeature &feat : std::as_const( featureList ) )
753 {
754 action.insertFeatureIds << QStringLiteral( "%1.%2" ).arg( typeName, QgsServerFeatureId::getServerFid( feat, pkAttributes ) );
755 }
756 }
757
758 //force restoration of original layer filters
759 filterRestorer.reset();
760 }
761
762 QgsFeatureList featuresFromGML( QDomNodeList featureNodeList, QgsVectorLayer *layer )
763 {
764 // Store the inserted features
765 QgsFeatureList featList;
766
767 const auto provider { layer->dataProvider() };
768 Q_ASSERT( provider );
769
770 // Get Layer Field Information
771 QgsFields fields = provider->fields();
772 const QMap<QString, int> fieldMap = provider->fieldNameMap();
773 QMap<QString, int>::const_iterator fieldMapIt;
774
775 for ( int i = 0; i < featureNodeList.count(); i++ )
776 {
777 QgsFeature feat( fields );
778
779 QDomElement featureElem = featureNodeList.at( i ).toElement();
780 QDomNode currentAttributeChild = featureElem.firstChild();
781 bool conversionSuccess = true;
782
783 while ( !currentAttributeChild.isNull() )
784 {
785 QDomElement currentAttributeElement = currentAttributeChild.toElement();
786 QString attrName = currentAttributeElement.localName();
787
788 if ( attrName != QLatin1String( "boundedBy" ) )
789 {
790 if ( attrName != QLatin1String( "geometry" ) ) //a normal attribute
791 {
792 fieldMapIt = fieldMap.find( attrName );
793 if ( fieldMapIt == fieldMap.constEnd() )
794 {
795 continue;
796 }
797
798 QgsField field = fields.at( fieldMapIt.value() );
799 QString attrValue = currentAttributeElement.text();
800 int attrType = field.type();
801
802 QgsMessageLog::logMessage( QStringLiteral( "attr: name=%1 idx=%2 value=%3" ).arg( attrName ).arg( fieldMapIt.value() ).arg( attrValue ) );
803
804 if ( attrType == QMetaType::Type::Int )
805 feat.setAttribute( fieldMapIt.value(), attrValue.toInt( &conversionSuccess ) );
806 else if ( attrType == QMetaType::Type::Double )
807 feat.setAttribute( fieldMapIt.value(), attrValue.toDouble( &conversionSuccess ) );
808 else
809 feat.setAttribute( fieldMapIt.value(), attrValue );
810
811 if ( !conversionSuccess )
812 {
813 throw QgsRequestNotWellFormedException( QStringLiteral( "Property conversion error on layer insert" ) );
814 }
815 }
816 else //a geometry attribute
817 {
818 const QgsOgcUtils::Context context { layer, provider->transformContext() };
819 QgsGeometry g = QgsOgcUtils::geometryFromGML( currentAttributeElement, context );
820 if ( g.isNull() )
821 {
822 throw QgsRequestNotWellFormedException( QStringLiteral( "Geometry from GML error on layer insert" ) );
823 }
824 feat.setGeometry( g );
825 }
826 }
827 currentAttributeChild = currentAttributeChild.nextSibling();
828 }
829 // update feature list
830 featList << feat;
831 }
832 return featList;
833 }
834
836 {
837 if ( !parameters.contains( QStringLiteral( "OPERATION" ) ) )
838 {
839 throw QgsRequestNotWellFormedException( QStringLiteral( "OPERATION parameter is mandatory" ) );
840 }
841 if ( parameters.value( QStringLiteral( "OPERATION" ) ).toUpper() != QLatin1String( "DELETE" ) )
842 {
843 throw QgsRequestNotWellFormedException( QStringLiteral( "Only DELETE value is defined for OPERATION parameter" ) );
844 }
845
846 // Verifying parameters mutually exclusive
847 if ( ( parameters.contains( QStringLiteral( "FEATUREID" ) )
848 && ( parameters.contains( QStringLiteral( "FILTER" ) ) || parameters.contains( QStringLiteral( "BBOX" ) ) ) )
849 || ( parameters.contains( QStringLiteral( "FILTER" ) ) && ( parameters.contains( QStringLiteral( "FEATUREID" ) ) || parameters.contains( QStringLiteral( "BBOX" ) ) ) )
850 || ( parameters.contains( QStringLiteral( "BBOX" ) ) && ( parameters.contains( QStringLiteral( "FEATUREID" ) ) || parameters.contains( QStringLiteral( "FILTER" ) ) ) ) )
851 {
852 throw QgsRequestNotWellFormedException( QStringLiteral( "FEATUREID FILTER and BBOX parameters are mutually exclusive" ) );
853 }
854
855 transactionRequest request;
856
857 QStringList typeNameList;
858 // parse FEATUREID
859 if ( parameters.contains( QStringLiteral( "FEATUREID" ) ) )
860 {
861 QStringList fidList = parameters.value( QStringLiteral( "FEATUREID" ) ).split( ',' );
862
863 QMap<QString, QStringList> fidsMap;
864
865 QStringList::const_iterator fidIt = fidList.constBegin();
866 for ( ; fidIt != fidList.constEnd(); ++fidIt )
867 {
868 // Get FeatureID
869 QString fid = *fidIt;
870 fid = fid.trimmed();
871 // testing typename in the WFS featureID
872 if ( !fid.contains( '.' ) )
873 {
874 throw QgsRequestNotWellFormedException( QStringLiteral( "FEATUREID has to have TYPENAME in the values" ) );
875 }
876
877 QString typeName = fid.section( '.', 0, 0 );
878 fid = fid.section( '.', 1, 1 );
879 if ( !typeNameList.contains( typeName ) )
880 {
881 typeNameList << typeName;
882 }
883
884 QStringList fids;
885 if ( fidsMap.contains( typeName ) )
886 {
887 fids = fidsMap.value( typeName );
888 }
889 fids.append( fid );
890 fidsMap.insert( typeName, fids );
891 }
892
893 QMap<QString, QStringList>::const_iterator fidsMapIt = fidsMap.constBegin();
894 while ( fidsMapIt != fidsMap.constEnd() )
895 {
896 transactionDelete action;
897 action.typeName = fidsMapIt.key();
898
899 action.serverFids = fidsMapIt.value();
901
902 request.deletes.append( action );
903 }
904 return request;
905 }
906
907 if ( !parameters.contains( QStringLiteral( "TYPENAME" ) ) )
908 {
909 throw QgsRequestNotWellFormedException( QStringLiteral( "TYPENAME is mandatory except if FEATUREID is used" ) );
910 }
911
912 typeNameList = parameters.value( QStringLiteral( "TYPENAME" ) ).split( ',' );
913
914 // Create actions based on TypeName
915 QStringList::const_iterator typeNameIt = typeNameList.constBegin();
916 for ( ; typeNameIt != typeNameList.constEnd(); ++typeNameIt )
917 {
918 QString typeName = *typeNameIt;
919 typeName = typeName.trimmed();
920
921 transactionDelete action;
922 action.typeName = typeName;
923
924 request.deletes.append( action );
925 }
926
927 // Manage extra parameter exp_filter
928 if ( parameters.contains( QStringLiteral( "EXP_FILTER" ) ) )
929 {
930 QString expFilterName = parameters.value( QStringLiteral( "EXP_FILTER" ) );
931 QStringList expFilterList;
932 const thread_local QRegularExpression rx( "\\(([^()]+)\\)" );
933 QRegularExpressionMatchIterator matchIt = rx.globalMatch( expFilterName );
934 if ( !matchIt.hasNext() )
935 {
936 expFilterList << expFilterName;
937 }
938 else
939 {
940 while ( matchIt.hasNext() )
941 {
942 const QRegularExpressionMatch match = matchIt.next();
943 if ( match.hasMatch() )
944 {
945 QStringList matches = match.capturedTexts();
946 matches.pop_front(); // remove whole match
947 expFilterList.append( matches );
948 }
949 }
950 }
951
952 // Verifying the 1:1 mapping between TYPENAME and EXP_FILTER but without exception
953 if ( request.deletes.size() == expFilterList.size() )
954 {
955 // set feature request filter expression based on filter element
956 QList<transactionDelete>::iterator dIt = request.deletes.begin();
957 QStringList::const_iterator expFilterIt = expFilterList.constBegin();
958 for ( ; dIt != request.deletes.end(); ++dIt )
959 {
960 transactionDelete &action = *dIt;
961 // Get Filter for this typeName
962 QString expFilter;
963 if ( expFilterIt != expFilterList.constEnd() )
964 {
965 expFilter = *expFilterIt;
966 }
967 std::shared_ptr<QgsExpression> filter( new QgsExpression( expFilter ) );
968 if ( filter )
969 {
970 if ( filter->hasParserError() )
971 {
972 QgsMessageLog::logMessage( filter->parserErrorString() );
973 }
974 else
975 {
976 if ( filter->needsGeometry() )
977 {
979 }
980 action.featureRequest.setFilterExpression( filter->expression() );
981 }
982 }
983 }
984 }
985 else
986 {
987 QgsMessageLog::logMessage( "There has to be a 1:1 mapping between each element in a TYPENAME and the EXP_FILTER list" );
988 }
989 }
990
991 if ( parameters.contains( QStringLiteral( "BBOX" ) ) )
992 {
993 // get bbox value
994 QString bbox = parameters.value( QStringLiteral( "BBOX" ) );
995 if ( bbox.isEmpty() )
996 {
997 throw QgsRequestNotWellFormedException( QStringLiteral( "BBOX parameter is empty" ) );
998 }
999
1000 // get bbox corners
1001 QStringList corners = bbox.split( ',' );
1002 if ( corners.size() != 4 )
1003 {
1004 throw QgsRequestNotWellFormedException( QStringLiteral( "BBOX has to be composed of 4 elements: '%1'" ).arg( bbox ) );
1005 }
1006
1007 // convert corners to double
1008 double d[4];
1009 bool ok;
1010 for ( int i = 0; i < 4; i++ )
1011 {
1012 corners[i].replace( ' ', '+' );
1013 d[i] = corners[i].toDouble( &ok );
1014 if ( !ok )
1015 {
1016 throw QgsRequestNotWellFormedException( QStringLiteral( "BBOX has to be composed of 4 double: '%1'" ).arg( bbox ) );
1017 }
1018 }
1019 // create extent
1020 QgsRectangle extent( d[0], d[1], d[2], d[3] );
1021
1022 // set feature request filter rectangle
1023 QList<transactionDelete>::iterator dIt = request.deletes.begin();
1024 for ( ; dIt != request.deletes.end(); ++dIt )
1025 {
1026 transactionDelete &action = *dIt;
1027 action.featureRequest.setFilterRect( extent );
1028 }
1029 return request;
1030 }
1031 else if ( parameters.contains( QStringLiteral( "FILTER" ) ) )
1032 {
1033 QString filterName = parameters.value( QStringLiteral( "FILTER" ) );
1034 QStringList filterList;
1035
1036 const thread_local QRegularExpression rx( "\\(([^()]+)\\)" );
1037 QRegularExpressionMatchIterator matchIt = rx.globalMatch( filterName );
1038 if ( !matchIt.hasNext() )
1039 {
1040 filterList << filterName;
1041 }
1042 else
1043 {
1044 while ( matchIt.hasNext() )
1045 {
1046 const QRegularExpressionMatch match = matchIt.next();
1047 if ( match.hasMatch() )
1048 {
1049 QStringList matches = match.capturedTexts();
1050 matches.pop_front(); // remove whole match
1051 filterList.append( matches );
1052 }
1053 }
1054 }
1055
1056 // Verifying the 1:1 mapping between TYPENAME and FILTER
1057 if ( request.deletes.size() != filterList.size() )
1058 {
1059 throw QgsRequestNotWellFormedException( QStringLiteral( "There has to be a 1:1 mapping between each element in a TYPENAME and the FILTER list" ) );
1060 }
1061
1062 // set feature request filter expression based on filter element
1063 QList<transactionDelete>::iterator dIt = request.deletes.begin();
1064 QStringList::const_iterator filterIt = filterList.constBegin();
1065 for ( ; dIt != request.deletes.end(); ++dIt )
1066 {
1067 transactionDelete &action = *dIt;
1068
1069 // Get Filter for this typeName
1070 QDomDocument filter;
1071 if ( filterIt != filterList.constEnd() )
1072 {
1073 QString errorMsg;
1074 if ( !filter.setContent( *filterIt, true, &errorMsg ) )
1075 {
1076 throw QgsRequestNotWellFormedException( QStringLiteral( "error message: %1. The XML string was: %2" ).arg( errorMsg, *filterIt ) );
1077 }
1078 }
1079
1080 QDomElement filterElem = filter.firstChildElement();
1081 QStringList serverFids;
1082 action.featureRequest = parseFilterElement( action.typeName, filterElem, serverFids, project );
1083 action.serverFids = serverFids;
1084
1085 if ( filterIt != filterList.constEnd() )
1086 {
1087 ++filterIt;
1088 }
1089 }
1090 return request;
1091 }
1092
1093 return request;
1094 }
1095
1096 transactionRequest parseTransactionRequestBody( QDomElement &docElem, const QgsProject *project )
1097 {
1098 transactionRequest request;
1099
1100 QDomNodeList docChildNodes = docElem.childNodes();
1101
1102 QDomElement actionElem;
1103 QString actionName;
1104
1105 for ( int i = docChildNodes.count(); 0 < i; --i )
1106 {
1107 actionElem = docChildNodes.at( i - 1 ).toElement();
1108 actionName = actionElem.localName();
1109
1110 if ( actionName == QLatin1String( "Insert" ) )
1111 {
1112 transactionInsert action = parseInsertActionElement( actionElem );
1113 request.inserts.append( action );
1114 }
1115 else if ( actionName == QLatin1String( "Update" ) )
1116 {
1117 transactionUpdate action = parseUpdateActionElement( actionElem, project );
1118 request.updates.append( action );
1119 }
1120 else if ( actionName == QLatin1String( "Delete" ) )
1121 {
1122 transactionDelete action = parseDeleteActionElement( actionElem, project );
1123 request.deletes.append( action );
1124 }
1125 }
1126
1127 return request;
1128 }
1129
1130 transactionDelete parseDeleteActionElement( QDomElement &actionElem, const QgsProject *project )
1131 {
1132 QString typeName = actionElem.attribute( QStringLiteral( "typeName" ) );
1133 if ( typeName.contains( ':' ) )
1134 typeName = typeName.section( ':', 1, 1 );
1135
1136 QDomElement filterElem = actionElem.firstChild().toElement();
1137 if ( filterElem.tagName() != QLatin1String( "Filter" ) )
1138 {
1139 throw QgsRequestNotWellFormedException( QStringLiteral( "Delete action element first child is not Filter" ) );
1140 }
1141
1142 QStringList serverFids;
1143 QgsFeatureRequest featureRequest = parseFilterElement( typeName, filterElem, serverFids, project );
1144
1145 transactionDelete action;
1146 action.typeName = typeName;
1147 action.featureRequest = featureRequest;
1148 action.serverFids = serverFids;
1149 action.error = false;
1150
1151 if ( actionElem.hasAttribute( QStringLiteral( "handle" ) ) )
1152 {
1153 action.handle = actionElem.attribute( QStringLiteral( "handle" ) );
1154 }
1155
1156 return action;
1157 }
1158
1159 transactionUpdate parseUpdateActionElement( QDomElement &actionElem, const QgsProject *project )
1160 {
1161 QString typeName = actionElem.attribute( QStringLiteral( "typeName" ) );
1162 if ( typeName.contains( ':' ) )
1163 typeName = typeName.section( ':', 1, 1 );
1164
1165 QDomNodeList propertyNodeList = actionElem.elementsByTagName( QStringLiteral( "Property" ) );
1166 if ( propertyNodeList.isEmpty() )
1167 {
1168 throw QgsRequestNotWellFormedException( QStringLiteral( "Update action element must have one or more Property element" ) );
1169 }
1170
1171 QMap<QString, QString> propertyMap;
1172 QDomElement propertyElem;
1173 QDomElement nameElem;
1174 QDomElement valueElem;
1175 QDomElement geometryElem;
1176
1177 for ( int l = 0; l < propertyNodeList.count(); ++l )
1178 {
1179 propertyElem = propertyNodeList.at( l ).toElement();
1180 nameElem = propertyElem.elementsByTagName( QStringLiteral( "Name" ) ).at( 0 ).toElement();
1181 valueElem = propertyElem.elementsByTagName( QStringLiteral( "Value" ) ).at( 0 ).toElement();
1182 if ( nameElem.text() != QLatin1String( "geometry" ) )
1183 {
1184 propertyMap.insert( nameElem.text(), valueElem.text() );
1185 }
1186 else
1187 {
1188 geometryElem = valueElem;
1189 }
1190 }
1191
1192 QDomNodeList filterNodeList = actionElem.elementsByTagName( QStringLiteral( "Filter" ) );
1193 QgsFeatureRequest featureRequest;
1194 QStringList serverFids;
1195 if ( filterNodeList.size() != 0 )
1196 {
1197 QDomElement filterElem = filterNodeList.at( 0 ).toElement();
1198 featureRequest = parseFilterElement( typeName, filterElem, serverFids, project );
1199 }
1200
1201 transactionUpdate action;
1202 action.typeName = typeName;
1203 action.propertyMap = propertyMap;
1204 action.geometryElement = geometryElem;
1205 action.featureRequest = featureRequest;
1206 action.serverFids = serverFids;
1207 action.error = false;
1208
1209 if ( actionElem.hasAttribute( QStringLiteral( "handle" ) ) )
1210 {
1211 action.handle = actionElem.attribute( QStringLiteral( "handle" ) );
1212 }
1213
1214 return action;
1215 }
1216
1218 {
1219 QDomNodeList featureNodeList = actionElem.childNodes();
1220 if ( featureNodeList.size() != 1 )
1221 {
1222 throw QgsRequestNotWellFormedException( QStringLiteral( "Insert action element must have one or more child node" ) );
1223 }
1224
1225 QString typeName;
1226 for ( int i = 0; i < featureNodeList.count(); ++i )
1227 {
1228 QString tempTypeName = featureNodeList.at( i ).toElement().localName();
1229 if ( tempTypeName.contains( ':' ) )
1230 tempTypeName = tempTypeName.section( ':', 1, 1 );
1231
1232 if ( typeName.isEmpty() )
1233 {
1234 typeName = tempTypeName;
1235 }
1236 else if ( tempTypeName != typeName )
1237 {
1238 throw QgsRequestNotWellFormedException( QStringLiteral( "Insert action element must have one typename features" ) );
1239 }
1240 }
1241
1242 transactionInsert action;
1243 action.typeName = typeName;
1244 action.featureNodeList = featureNodeList;
1245 action.error = false;
1246
1247 if ( actionElem.hasAttribute( QStringLiteral( "handle" ) ) )
1248 {
1249 action.handle = actionElem.attribute( QStringLiteral( "handle" ) );
1250 }
1251
1252 return action;
1253 }
1254
1255 namespace
1256 {
1257
1258 void addTransactionResult( QDomDocument &responseDoc, QDomElement &responseElem, const QString &status, const QString &locator, const QString &message )
1259 {
1260 QDomElement trElem = responseDoc.createElement( QStringLiteral( "TransactionResult" ) );
1261 QDomElement stElem = responseDoc.createElement( QStringLiteral( "Status" ) );
1262 QDomElement successElem = responseDoc.createElement( status );
1263 stElem.appendChild( successElem );
1264 trElem.appendChild( stElem );
1265 responseElem.appendChild( trElem );
1266
1267 if ( !locator.isEmpty() )
1268 {
1269 QDomElement locElem = responseDoc.createElement( QStringLiteral( "Locator" ) );
1270 locElem.appendChild( responseDoc.createTextNode( locator ) );
1271 trElem.appendChild( locElem );
1272 }
1273
1274 if ( !message.isEmpty() )
1275 {
1276 QDomElement mesElem = responseDoc.createElement( QStringLiteral( "Message" ) );
1277 mesElem.appendChild( responseDoc.createTextNode( message ) );
1278 trElem.appendChild( mesElem );
1279 }
1280 }
1281
1282 } // namespace
1283
1284 } // namespace v1_0_0
1285} // 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...
Class for parsing and evaluation of expressions (formerly called "search strings").
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
This class wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setFlags(Qgis::FeatureRequestFlags flags)
Sets flags that affect how features will be fetched.
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
QgsFeatureRequest & setExpressionContext(const QgsExpressionContext &context)
Sets the expression context used to evaluate filter expressions.
QgsFeatureRequest & setFilterRect(const QgsRectangle &rectangle)
Sets the rectangle from which features will be taken.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h: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:76
QString id
Definition qgsmaplayer.h:79
Qgis::LayerType type
Definition qgsmaplayer.h:86
QgsCoordinateTransformContext transformContext() const
Returns the layer data provider coordinate transform context or a default transform context if the la...
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
RAII class to restore layer filters on destruction.
static void applyAccessControlLayerFilters(const QgsAccessControl *accessControl, QgsMapLayer *mapLayer, QHash< QgsMapLayer *, QString > &originalLayerFilters)
Apply filter from AccessControl.
Exception base class for service exceptions.
QString message() const
Returns the exception message.
static QgsGeometry geometryFromGML(const QString &xmlString, const QgsOgcUtils::Context &context=QgsOgcUtils::Context())
Static method that creates geometry from GML.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition qgsproject.h:107
Q_INVOKABLE QgsMapLayer * mapLayer(const QString &layerId) const
Retrieve a pointer to a registered layer by layer ID.
A rectangle specified with double values.
QgsServerInterface Class defining interfaces exposed by QGIS Server and made available to plugins.
virtual QgsAccessControl * accessControls() const =0
Gets the registered access control filters.
QgsServerRequest Class defining request interface passed to services QgsService::executeRequest() met...
QgsServerRequest::Parameters parameters() const
Returns a map of query parameters with keys converted to uppercase.
QMap< QString, QString > Parameters
virtual QByteArray data() const
Returns post/put data Check for QByteArray::isNull() to check if data is available.
QgsServerResponse Class defining response interface passed to services QgsService::executeRequest() m...
virtual void write(const QString &data)
Write string This is a convenient method that will write directly to the underlying I/O device.
virtual void setHeader(const QString &key, const QString &value)=0
Set Header entry Add Header entry to the response Note that it is usually an error to set Header afte...
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
This is the base class for vector data providers.
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 data sets.
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.
SERVER_EXPORT QgsFeatureRequest updateFeatureRequestFromServerFids(QgsFeatureRequest &featureRequest, const QStringList &serverFids, const QgsVectorDataProvider *provider)
Returns the feature request based on feature ids build with primary keys.
SERVER_EXPORT QString getServerFid(const QgsFeature &feature, const QgsAttributeList &pkAttributes)
Returns the feature id based on primary keys.
SERVER_EXPORT QStringList wfsLayerIds(const QgsProject &project)
Returns the Layer ids list defined in a QGIS project as published in WFS.
SERVER_EXPORT QStringList wfstUpdateLayerIds(const QgsProject &project)
Returns the Layer ids list defined in a QGIS project as published as WFS-T with update capabilities.
SERVER_EXPORT QStringList wfstInsertLayerIds(const QgsProject &project)
Returns the Layer ids list defined in a QGIS project as published as WFS-T with insert capabilities.
SERVER_EXPORT QStringList wfstDeleteLayerIds(const QgsProject &project)
Returns the Layer ids list defined in a QGIS project as published as WFS-T with delete capabilities.
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.
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