Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions app/inpututils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,14 @@ QString InputUtils::createUniqueProjectDirectory( const QString &baseDataDir, co
return projectDirPath;
}

bool InputUtils::removeDir( const QString &dir )
{
if ( dir.isEmpty() || dir == "/" )
return false;

return QDir( dir ).removeRecursively();
}

void InputUtils::onQgsLogMessageReceived( const QString &message, const QString &tag, Qgis::MessageLevel level )
{
QString levelStr;
Expand Down Expand Up @@ -528,6 +536,39 @@ QString InputUtils::renameWithDateTime( const QString &srcPath, const QDateTime
return QString();
}

QDateTime InputUtils::getLastModifiedFileDateTime( const QString &path )
{
QDateTime lastModified;
QDirIterator it( path, QStringList() << QStringLiteral( "*" ), QDir::Files, QDirIterator::Subdirectories );
while ( it.hasNext() )
{
it.next();
if ( !MerginApi::isInIgnore( it.fileInfo() ) )
{
if ( it.fileInfo().lastModified() > lastModified )
{
lastModified = it.fileInfo().lastModified();
}
}
}
return lastModified.toUTC();
}

int InputUtils::getProjectFilesCount( const QString &path )
{
int count = 0;
QDirIterator it( path, QStringList() << QStringLiteral( "*" ), QDir::Files, QDirIterator::Subdirectories );
while ( it.hasNext() )
{
it.next();
if ( !MerginApi::isInIgnore( it.fileInfo() ) )
{
count++;
}
}
return count;
}

QString InputUtils::downloadInProgressFilePath( const QString &projectDir )
{
return projectDir + "/.mergin/.project.downloading";
Expand Down
6 changes: 6 additions & 0 deletions app/inpututils.h
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,12 @@ class InputUtils: public QObject
//! Creates a unique project directory for given project name (used for initial download of a project)
static QString createUniqueProjectDirectory( const QString &baseDataDir, const QString &projectName );

static bool removeDir( const QString &projectDir );

static QDateTime getLastModifiedFileDateTime( const QString &path );

static int getProjectFilesCount( const QString &path );

signals:
Q_INVOKABLE void showNotificationRequested( const QString &message );

Expand Down
222 changes: 47 additions & 175 deletions app/localprojectsmanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,18 @@
LocalProjectsManager::LocalProjectsManager( const QString &dataDir )
: mDataDir( dataDir )
{
reloadProjectDir();
reloadDataDir();
}

void LocalProjectsManager::reloadProjectDir() // TODO: maybe add function to reload one specific project
void LocalProjectsManager::reloadDataDir()
{
mProjects.clear();
QStringList entryList = QDir( mDataDir ).entryList( QDir::NoDotAndDotDot | QDir::Dirs );
for ( QString folderName : entryList )
for ( const QString &folderName : entryList )
{
LocalProjectInfo info;
LocalProject info;
info.projectDir = mDataDir + "/" + folderName;
info.qgisProjectFilePath = findQgisProjectFile( info.projectDir, info.qgisProjectError );
info.qgisProjectFilePath = findQgisProjectFile( info.projectDir, info.projectError );

MerginProjectMetadata metadata = MerginProjectMetadata::fromCachedJson( info.projectDir + "/" + MerginApi::sMetadataFile );
if ( metadata.isValid() )
Expand All @@ -47,183 +47,119 @@ void LocalProjectsManager::reloadProjectDir() // TODO: maybe add function to rel
mProjects << info;
}

qDebug() << "LocalProjectsManager: found" << mProjects.size() << "projects";
qDebug() << "Found " << mProjects.size() << " local projects in " << mDataDir;
emit dataDirReloaded();
}

LocalProjectInfo LocalProjectsManager::projectFromDirectory( const QString &projectDir ) const
LocalProject LocalProjectsManager::projectFromDirectory( const QString &projectDir ) const
{
for ( const LocalProjectInfo &info : mProjects )
for ( const LocalProject &info : mProjects )
{
if ( info.projectDir == projectDir )
return info;
}
return LocalProjectInfo();
return LocalProject();
}

LocalProjectInfo LocalProjectsManager::projectFromProjectFilePath( const QString &projectFilePath ) const
LocalProject LocalProjectsManager::projectFromProjectFilePath( const QString &projectFilePath ) const
{
for ( const LocalProjectInfo &info : mProjects )
for ( const LocalProject &info : mProjects )
{
if ( info.qgisProjectFilePath == projectFilePath )
return info;
}
return LocalProjectInfo();
return LocalProject();
}

LocalProjectInfo LocalProjectsManager::projectFromMerginName( const QString &projectFullName ) const
LocalProject LocalProjectsManager::projectFromMerginName( const QString &projectFullName ) const
{
for ( const LocalProjectInfo &info : mProjects )
for ( const LocalProject &info : mProjects )
{
if ( MerginApi::getFullProjectName( info.projectNamespace, info.projectName ) == projectFullName )
if ( info.id() == projectFullName )
return info;
}
return LocalProjectInfo();
return LocalProject();
}

LocalProjectInfo LocalProjectsManager::projectFromMerginName( const QString &projectNamespace, const QString &projectName ) const
LocalProject LocalProjectsManager::projectFromMerginName( const QString &projectNamespace, const QString &projectName ) const
{
return projectFromMerginName( MerginApi::getFullProjectName( projectNamespace, projectName ) );
}

bool LocalProjectsManager::hasMerginProject( const QString &projectFullName ) const
{
return projectFromMerginName( projectFullName ).isValid();
}

bool LocalProjectsManager::hasMerginProject( const QString &projectNamespace, const QString &projectName ) const
{
return hasMerginProject( MerginApi::getFullProjectName( projectNamespace, projectName ) );
}

void LocalProjectsManager::updateProjectStatus( const QString &projectDir )
{
for ( LocalProjectInfo &info : mProjects )
{
if ( info.projectDir == projectDir )
{
updateProjectStatus( info );
return;
}
}
Q_ASSERT( false ); // should not happen
}

void LocalProjectsManager::addProject( const QString &projectDir, const QString &projectNamespace, const QString &projectName )
void LocalProjectsManager::addLocalProject( const QString &projectDir, const QString &projectName )
{
LocalProjectInfo project;
project.projectDir = projectDir;
project.qgisProjectFilePath = findQgisProjectFile( projectDir, project.qgisProjectError );
project.projectNamespace = projectNamespace;
project.projectName = projectName;

mProjects << project;
addProject( projectDir, QString(), projectName );
}

void LocalProjectsManager::addMerginProject( const QString &projectDir, const QString &projectNamespace, const QString &projectName )
{
addProject( projectDir, projectNamespace, projectName );
// version info and status should be updated afterwards
emit localMerginProjectAdded( projectDir );
}

void LocalProjectsManager::addLocalProject( const QString &projectDir, const QString &projectName )
{
addProject( projectDir, QString(), projectName );
emit localProjectAdded( projectDir );
}

void LocalProjectsManager::removeProject( const QString &projectDir )
void LocalProjectsManager::removeLocalProject( const QString &projectId )
{
for ( int i = 0; i < mProjects.count(); ++i )
{
if ( mProjects[i].projectDir == projectDir )
if ( mProjects[i].id() == projectId )
{
emit aboutToRemoveLocalProject( mProjects[i] );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see that this signal is propagated to ProjectsModel. Does it have some advantage to emit it before project is removed from a disk and local manager's project?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think generally it is good idea to first remove any reference to the project and then actually remove it from device (in case we would want to check if the dir exists or not). Do not have a preference though


InputUtils::removeDir( mProjects[i].projectDir );
mProjects.removeAt( i );
emit localProjectRemoved( projectDir );

return;
}
}
}

void LocalProjectsManager::resetMerginInfo( const QString &projectNamespace, const QString &projectName )
bool LocalProjectsManager::projectIsValid( const QString &path ) const
{
for ( int i = 0; i < mProjects.count(); ++i )
{
if ( mProjects[i].projectNamespace == projectNamespace && mProjects[i].projectName == projectName )
if ( mProjects[i].qgisProjectFilePath == path )
{
mProjects[i].localVersion = -1;
mProjects[i].serverVersion = -1;
mProjects[i].projectNamespace.clear();
updateProjectStatus( mProjects[i] );
emit projectMetadataChanged( mProjects[i].projectDir );
return;
return mProjects[i].projectError.isEmpty();
}
}
return false;
}

void LocalProjectsManager::deleteProjectDirectory( const QString &projectDir )
QString LocalProjectsManager::projectId( const QString &path ) const
{
for ( int i = 0; i < mProjects.count(); ++i )
{
if ( mProjects[i].projectDir == projectDir )
if ( mProjects[i].qgisProjectFilePath == path )
{
Q_ASSERT( !projectDir.isEmpty() && projectDir != "/" );
QDir( projectDir ).removeRecursively();
mProjects.removeAt( i );
return;
return mProjects[i].id();
}
}
return QString();
}

void LocalProjectsManager::updateMerginLocalVersion( const QString &projectDir, int version )
void LocalProjectsManager::updateLocalVersion( const QString &projectDir, int version )
{
for ( int i = 0; i < mProjects.count(); ++i )
{
if ( mProjects[i].projectDir == projectDir )
{
mProjects[i].localVersion = version;
updateProjectStatus( mProjects[i] );
return;
}
}
Q_ASSERT( false ); // should not happen
}

void LocalProjectsManager::updateMerginServerVersion( const QString &projectDir, int version )
{
for ( int i = 0; i < mProjects.count(); ++i )
{
if ( mProjects[i].projectDir == projectDir )
{
mProjects[i].serverVersion = version;
updateProjectStatus( mProjects[i] );
emit localProjectDataChanged( mProjects[i] );
return;
}
}
Q_ASSERT( false ); // should not happen
}

void LocalProjectsManager::updateProjectErrors( const QString &projectDir, const QString &errMsg )
{
for ( int i = 0; i < mProjects.count(); ++i )
{
if ( mProjects[i].projectDir == projectDir )
{
// Effects only local project list, no need to send projectMetadataChanged
mProjects[i].qgisProjectError = errMsg;
return;
}
}
}

void LocalProjectsManager::updateMerginNamespace( const QString &projectDir, const QString &projectNamespace )
void LocalProjectsManager::updateNamespace( const QString &projectDir, const QString &projectNamespace )
{
for ( int i = 0; i < mProjects.count(); ++i )
{
if ( mProjects[i].projectDir == projectDir )
{
// Effects only local project list, no need to send projectMetadataChanged
mProjects[i].projectNamespace = projectNamespace;

emit localProjectDataChanged( mProjects[i] );
return;
}
}
Expand Down Expand Up @@ -267,78 +203,14 @@ QString LocalProjectsManager::findQgisProjectFile( const QString &projectDir, QS
return QString();
}


static QDateTime _getLastModifiedFileDateTime( const QString &path )
{
QDateTime lastModified;
QDirIterator it( path, QStringList() << QStringLiteral( "*" ), QDir::Files, QDirIterator::Subdirectories );
while ( it.hasNext() )
{
it.next();
if ( !MerginApi::isInIgnore( it.fileInfo() ) )
{
if ( it.fileInfo().lastModified() > lastModified )
{
lastModified = it.fileInfo().lastModified();
}
}
}
return lastModified.toUTC();
}

static int _getProjectFilesCount( const QString &path )
{
int count = 0;
QDirIterator it( path, QStringList() << QStringLiteral( "*" ), QDir::Files, QDirIterator::Subdirectories );
while ( it.hasNext() )
{
it.next();
if ( !MerginApi::isInIgnore( it.fileInfo() ) )
{
count++;
}
}
return count;
}

ProjectStatus LocalProjectsManager::currentProjectStatus( const LocalProjectInfo &project )
void LocalProjectsManager::addProject( const QString &projectDir, const QString &projectNamespace, const QString &projectName )
{
// There was no sync yet
if ( project.localVersion < 0 )
{
return ProjectStatus::NoVersion;
}

//
// TODO: this check for local modifications should be revisited
//

// Something has locally changed after last sync with server
QString metadataFilePath = project.projectDir + "/" + MerginApi::sMetadataFile;
QDateTime lastModified = _getLastModifiedFileDateTime( project.projectDir );
QDateTime lastSync = QFileInfo( metadataFilePath ).lastModified();
MerginProjectMetadata meta = MerginProjectMetadata::fromCachedJson( metadataFilePath );
int filesCount = _getProjectFilesCount( project.projectDir );
if ( lastSync < lastModified || meta.files.count() != filesCount )
{
return ProjectStatus::Modified;
}

// Version is lower than latest one, last sync also before updated
if ( project.localVersion < project.serverVersion )
{
return ProjectStatus::OutOfDate;
}

return ProjectStatus::UpToDate;
}
LocalProject project;
project.projectDir = projectDir;
project.qgisProjectFilePath = findQgisProjectFile( projectDir, project.projectError );
project.projectName = projectName;
project.projectNamespace = projectNamespace;

void LocalProjectsManager::updateProjectStatus( LocalProjectInfo &project )
{
ProjectStatus newStatus = currentProjectStatus( project );
if ( newStatus != project.status )
{
project.status = newStatus;
emit projectMetadataChanged( project.projectDir );
}
mProjects << project;
emit localProjectAdded( project );
}
Loading