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