Skip to content

Commit

Permalink
Merge branch 'master' into enhancement/hidingTrackLayerOnRecord
Browse files Browse the repository at this point in the history
  • Loading branch information
VitorVieiraZ committed Jan 23, 2025
2 parents 0e7fe48 + 6115084 commit f604511
Show file tree
Hide file tree
Showing 18 changed files with 625 additions and 45 deletions.
16 changes: 16 additions & 0 deletions app/activeproject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,9 @@ bool ActiveProject::forceLoad( const QString &filePath, bool force )
CoreUtils::log( QStringLiteral( "Project load" ), QStringLiteral( "Could not find project in local projects: " ) + filePath );
}

QString role = MerginProjectMetadata::fromCachedJson( CoreUtils::getProjectMetadataPath( mLocalProject.projectDir ) ).role;
setProjectRole( role );

updateMapTheme();
updateActiveLayer();
updateMapSettingsLayers();
Expand Down Expand Up @@ -548,4 +551,17 @@ bool ActiveProject::projectHasRecordingLayers() const
}

return false;
QString ActiveProject::projectRole() const
{
return mProjectRole;
}

void ActiveProject::setProjectRole( const QString &role )
{
if ( mProjectRole != role )
{
mProjectRole = role;

emit projectRoleChanged();
}
}
11 changes: 10 additions & 1 deletion app/activeproject.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "localprojectsmanager.h"
#include "autosynccontroller.h"
#include "inputmapsettings.h"
#include "merginprojectmetadata.h"

/**
* \brief The ActiveProject class can load a QGIS project and holds its data.
Expand All @@ -33,6 +34,7 @@ class ActiveProject: public QObject
Q_PROPERTY( QgsProject *qgsProject READ qgsProject NOTIFY qgsProjectChanged ) // QgsProject instance of active project, never changes
Q_PROPERTY( AutosyncController *autosyncController READ autosyncController NOTIFY autosyncControllerChanged )
Q_PROPERTY( InputMapSettings *mapSettings READ mapSettings WRITE setMapSettings NOTIFY mapSettingsChanged )
Q_PROPERTY( QString projectRole READ projectRole WRITE setProjectRole NOTIFY projectRoleChanged )

Q_PROPERTY( QString mapTheme READ mapTheme WRITE setMapTheme NOTIFY mapThemeChanged )
Q_PROPERTY( bool positionTrackingSupported READ positionTrackingSupported NOTIFY positionTrackingSupportedChanged )
Expand Down Expand Up @@ -114,6 +116,11 @@ class ActiveProject: public QObject

//! Returns true if the project has at least one layer that allows recording
Q_INVOKABLE bool projectHasRecordingLayers() const;
/**
* Returns role/permission level of current user for this project
*/
Q_INVOKABLE QString projectRole() const;
void setProjectRole( const QString &role );

signals:
void qgsProjectChanged();
Expand Down Expand Up @@ -142,6 +149,8 @@ class ActiveProject: public QObject
// Emited when the app (UI) should show tracking because there is a running tracking service
void startPositionTracking();

void projectRoleChanged();

public slots:
// Reloads project if current project path matches given path (its the same project)
bool reloadProject( QString projectDir );
Expand Down Expand Up @@ -178,10 +187,10 @@ class ActiveProject: public QObject
ActiveLayer &mActiveLayer;
LocalProjectsManager &mLocalProjectsManager;
InputMapSettings *mMapSettings = nullptr;

std::unique_ptr<AutosyncController> mAutosyncController;

QString mProjectLoadingLog;
QString mProjectRole;

/**
* Reloads project.
Expand Down
21 changes: 21 additions & 0 deletions app/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -567,6 +567,27 @@ int main( int argc, char *argv[] )
syncManager.syncProject( project, SyncOptions::Authorized, SyncOptions::Retry );
} );

QObject::connect( &activeProject, &ActiveProject::projectReloaded, &lambdaContext, [merginApi = ma.get(), &activeProject]()
{
merginApi->reloadProjectRole( activeProject.projectFullName() );
} );

QObject::connect( ma.get(), &MerginApi::authChanged, &lambdaContext, [merginApi = ma.get(), &activeProject]()
{
if ( activeProject.isProjectLoaded() )
{
merginApi->reloadProjectRole( activeProject.projectFullName() );
}
} );

QObject::connect( ma.get(), &MerginApi::projectRoleUpdated, &activeProject, [&activeProject]( const QString & projectFullName, const QString & role )
{
if ( projectFullName == activeProject.projectFullName() )
{
activeProject.setProjectRole( role );
}
} );

QObject::connect( ma.get(), &MerginApi::notifyInfo, &lambdaContext, [&notificationModel]( const QString & message )
{
notificationModel.addInfo( message );
Expand Down
4 changes: 2 additions & 2 deletions app/qml/form/MMFormPage.qml
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ Page {

footer: MMComponents.MMToolbar {

visible: !root.layerIsReadOnly
visible: !root.layerIsReadOnly && __activeProject.projectRole !== "reader"

ObjectModel {
id: readStateButtons
Expand Down Expand Up @@ -231,7 +231,7 @@ Page {
id: editGeometry
text: qsTr( "Edit geometry" )
iconSource: __style.editIcon
visible: root.layerIsSpatial
visible: root.layerIsSpatial && __activeProject.projectRole !== "reader"
onClicked: root.editGeometryRequested( root.controller.featureLayerPair )
}
}
Expand Down
2 changes: 1 addition & 1 deletion app/qml/form/MMPreviewDrawer.qml
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ Item {
property bool isHTMLType: root.controller.type === MM.AttributePreviewController.HTML
property bool isEmptyType: root.controller.type === MM.AttributePreviewController.Empty

property bool showEditButton: !root.layerIsReadOnly
property bool showEditButton: !root.layerIsReadOnly && __activeProject.projectRole !== "reader"
property bool showStakeoutButton: __inputUtils.isPointLayerFeature( controller.featureLayerPair )
property bool showButtons: showEditButton || showStakeoutButton

Expand Down
1 change: 1 addition & 0 deletions app/qml/form/components/MMFeaturesListPageDrawer.qml
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ Drawer {
}

text: qsTr( "Add feature" )
visible: __activeProject.projectRole !== "reader"

onClicked: root.buttonClicked()
}
Expand Down
2 changes: 1 addition & 1 deletion app/qml/layers/MMFeaturesListPage.qml
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ MMComponents.MMPage {
anchors.bottom: parent.bottom
anchors.bottomMargin: root.hasToolbar ? __style.margin20 : ( __style.safeAreaBottom + __style.margin8 )

visible: __inputUtils.isNoGeometryLayer( root.selectedLayer ) && !root.layerIsReadOnly
visible: __inputUtils.isNoGeometryLayer( root.selectedLayer ) && !root.layerIsReadOnly && __activeProject.projectRole !== "reader"

text: qsTr("Add feature")

Expand Down
1 change: 1 addition & 0 deletions app/qml/main.qml
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,7 @@ ApplicationWindow {

text: qsTr("Add")
iconSource: __style.addIcon
visible: __activeProject.projectRole !== "reader"
onClicked: {
if ( __activeProject.projectHasRecordingLayers() ) {
stateManager.state = "map"
Expand Down
80 changes: 80 additions & 0 deletions app/test/testcoreutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -272,3 +272,83 @@ void TestCoreUtils::testNameAbbr()
QCOMPARE( CoreUtils::nameAbbr( name, email ), test.second );
}
}

void TestCoreUtils::testReplaceValueInJson()
{
// temporary test file
QString testFilePath = QDir::tempPath() + "/test_replace_value.json";

// basic replacement in valid JSON with int value
{
QFile file( testFilePath );
QVERIFY( file.open( QIODevice::WriteOnly ) );
file.write( R"({"name": "test", "value": 123})" );
file.close();

QVERIFY( CoreUtils::replaceValueInJson( testFilePath, "value", 456 ) );

// verify
QVERIFY( file.open( QIODevice::ReadOnly ) );
QJsonDocument doc = QJsonDocument::fromJson( file.readAll() );
file.close();
QVERIFY( doc.isObject() );
QJsonObject obj = doc.object();
QCOMPARE( obj["value"].toInt(), 456 );
QCOMPARE( obj["name"].toString(), QString( "test" ) );
}
// valid JSON with string value
{
QFile file( testFilePath );
QVERIFY( file.open( QIODevice::WriteOnly ) );
file.write( R"({"name": "test", "status": "active"})" );
file.close();

QVERIFY( CoreUtils::replaceValueInJson( testFilePath, "status", "inactive" ) );

// verify replacement
QVERIFY( file.open( QIODevice::ReadOnly ) );
QJsonDocument doc = QJsonDocument::fromJson( file.readAll() );
file.close();
QVERIFY( doc.isObject() );
QJsonObject obj = doc.object();
QCOMPARE( obj["status"].toString(), QString( "inactive" ) );
QCOMPARE( obj["name"].toString(), QString( "test" ) );
}

// add new key-value pair
{
QFile file( testFilePath );
QVERIFY( file.open( QIODevice::WriteOnly ) );
file.write( R"({"name": "test"})" );
file.close();

QVERIFY( CoreUtils::replaceValueInJson( testFilePath, "newKey", "newValue" ) );

// verify the addition
QVERIFY( file.open( QIODevice::ReadOnly ) );
QJsonDocument doc = QJsonDocument::fromJson( file.readAll() );
file.close();
QVERIFY( doc.isObject() );
QJsonObject obj = doc.object();
QCOMPARE( obj["newKey"].toString(), QString( "newValue" ) );
QCOMPARE( obj["name"].toString(), QString( "test" ) );
}

// invalid JSON file
{
QFile file( testFilePath );
QVERIFY( file.open( QIODevice::WriteOnly ) );
file.write( "invalid json content" );
file.close();

QVERIFY( !CoreUtils::replaceValueInJson( testFilePath, "key", "value" ) );
}

// non-existent file
{
QString nonExistentPath = QDir::tempPath() + "/non_existent.json";
QVERIFY( !CoreUtils::replaceValueInJson( nonExistentPath, "key", "value" ) );
}

QFile::remove( testFilePath );
}
1 change: 1 addition & 0 deletions app/test/testcoreutils.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class TestCoreUtils : public QObject
void testHasProjectFileExtension();
void testNameValidation();
void testNameAbbr();
void testReplaceValueInJson();
};

#endif // TESTCOREUTILS_H
Loading

0 comments on commit f604511

Please sign in to comment.