Skip to content

Commit

Permalink
Merge pull request #43560 from pblottiere/as_always_save_file
Browse files Browse the repository at this point in the history
Limit the damage when the temporary directory with qgd file is deleted
  • Loading branch information
pblottiere committed Jun 21, 2021
2 parents 00536e8 + 1552518 commit db4ddb9
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 7 deletions.
7 changes: 7 additions & 0 deletions python/core/auto_generated/qgsarchive.sip.in
Expand Up @@ -86,6 +86,13 @@ Returns the list of files within this archive
QString dir() const;
%Docstring
Returns the current temporary directory.
%End

bool exists() const;
%Docstring
Returns ``True`` if the archive exists on the filesystem, ``False`` otherwise.

.. versionadded:: 3.20
%End

};
Expand Down
3 changes: 2 additions & 1 deletion src/app/qgisapp.cpp
Expand Up @@ -7301,6 +7301,7 @@ bool QgisApp::fileSave()
QMessageBox::critical( this,
tr( "Unable to save project %1" ).arg( QDir::toNativeSeparators( QgsProject::instance()->fileName() ) ),
QgsProject::instance()->error() );
mProjectLastModified = QgsProject::instance()->lastModified();
return false;
}

Expand Down Expand Up @@ -7363,7 +7364,6 @@ void QgisApp::fileSaveAs()
mStatusBar->showMessage( tr( "Saved project to: %1" ).arg( QDir::toNativeSeparators( QgsProject::instance()->fileName() ) ), 5000 );
// add this to the list of recently used project files
saveRecentProjectPath();
mProjectLastModified = fullPath.lastModified();
}
else
{
Expand All @@ -7373,6 +7373,7 @@ void QgisApp::fileSaveAs()
QMessageBox::Ok,
Qt::NoButton );
}
mProjectLastModified = fullPath.lastModified();
} // QgisApp::fileSaveAs

void QgisApp::dxfExport()
Expand Down
37 changes: 31 additions & 6 deletions src/core/project/qgsproject.cpp
Expand Up @@ -3341,25 +3341,50 @@ bool QgsProject::zip( const QString &filename )
const QFileInfo info( qgsFile );
const QString asFileName = info.path() + QDir::separator() + info.completeBaseName() + "." + QgsAuxiliaryStorage::extension();

bool auxiliaryStorageSavedOk = true;
if ( ! saveAuxiliaryStorage( asFileName ) )
{
const QString err = mAuxiliaryStorage->errorString();
setError( tr( "Unable to save auxiliary storage ('%1')" ).arg( err ) );
return false;
setError( tr( "Unable to save auxiliary storage file ('%1'). The project has been saved but the latest changes to auxiliary data cannot be recovered. It is recommended to reload the project." ).arg( err ) );
auxiliaryStorageSavedOk = false;

// fixes the current archive and keep the previous version of qgd
if ( !mArchive->exists() )
{
mArchive.reset( new QgsProjectArchive() );
mArchive->unzip( mFile.fileName() );
mArchive->clearProjectFile();

const QString auxiliaryStorageFile = mArchive->auxiliaryStorageFile();
if ( ! auxiliaryStorageFile.isEmpty() )
{
archive->addFile( auxiliaryStorageFile );
mAuxiliaryStorage.reset( new QgsAuxiliaryStorage( auxiliaryStorageFile, false ) );
}
}
}
else
{
// in this case, an empty filename means that the auxiliary database is
// empty, so we don't want to save it
if ( QFile::exists( asFileName ) )
{
archive->addFile( asFileName );
}
}

// create the archive
archive->addFile( qgsFile.fileName() );
archive->addFile( asFileName );

// zip
bool zipOk = true;
if ( !archive->zip( filename ) )
{
setError( tr( "Unable to perform zip" ) );
return false;
zipOk = false;
}

return true;
return auxiliaryStorageSavedOk && zipOk;
}

bool QgsProject::isZipped() const
Expand Down Expand Up @@ -3540,7 +3565,7 @@ bool QgsProject::saveAuxiliaryStorage( const QString &filename )
}
}

if ( !mAuxiliaryStorage->exists( *this ) && filename.isEmpty() && empty )
if ( !mAuxiliaryStorage->exists( *this ) && empty )
{
return true; // it's not an error
}
Expand Down
5 changes: 5 additions & 0 deletions src/core/qgsarchive.cpp
Expand Up @@ -126,6 +126,11 @@ QStringList QgsArchive::files() const
return mFiles;
}

bool QgsArchive::exists() const
{
return QFileInfo::exists( mDir->path() );
}

QString QgsProjectArchive::projectFile() const
{
const auto constFiles = files();
Expand Down
6 changes: 6 additions & 0 deletions src/core/qgsarchive.h
Expand Up @@ -98,6 +98,12 @@ class CORE_EXPORT QgsArchive
*/
QString dir() const;

/**
* Returns TRUE if the archive exists on the filesystem, FALSE otherwise.
* \since QGIS 3.20
*/
bool exists() const;

private:
// content of the archive
QStringList mFiles;
Expand Down
44 changes: 44 additions & 0 deletions tests/src/python/test_qgsauxiliarystorage.py
Expand Up @@ -23,6 +23,7 @@
QgsPropertyDefinition,
QgsProperty,
QgsProject,
QgsProjectArchive,
QgsFeatureRequest,
QgsPalLayerSettings,
QgsSymbolLayer,
Expand Down Expand Up @@ -556,6 +557,49 @@ def testInvalidPrimaryKey(self):
self.assertEqual(al, None)
self.assertTrue("CREATE TABLE IF NOT EXISTS" in s.errorString())

def testQgdCreationInQgz(self):
# New project
p = QgsProject()
self.assertTrue(p.auxiliaryStorage().isValid())

# Save the project
path = tmpPath()
qgz = path + '.qgz'
self.assertTrue(p.write(qgz))
self.assertTrue(os.path.exists(qgz))

# Check the content of the archive: auxiliary database doesn't exist
# because it's empty
archive = QgsProjectArchive()
archive.unzip(qgz)
self.assertEqual(archive.auxiliaryStorageFile(), "")

# Add a vector layer and an auxiliary layer in the project
vl = createLayer()
self.assertTrue(vl.isValid())
p.addMapLayers([vl])

pkf = vl.fields().field(vl.fields().indexOf('pk'))
al = p.auxiliaryStorage().createAuxiliaryLayer(pkf, vl)
self.assertTrue(al.isValid())
vl.setAuxiliaryLayer(al)

# Add an auxiliary field to have a non empty auxiliary storage
pdef = QgsPropertyDefinition('propname', QgsPropertyDefinition.DataTypeNumeric, '', '', 'ut')
self.assertTrue(al.addAuxiliaryField(pdef))

# Save the project
path = tmpPath()
qgz = path + '.qgz'
self.assertTrue(p.write(qgz))
self.assertTrue(os.path.exists(qgz))

# Check the content of the archive: auxiliary database exist
# because it's not empty
archive = QgsProjectArchive()
archive.unzip(qgz)
self.assertNotEqual(archive.auxiliaryStorageFile(), '')


if __name__ == '__main__':
unittest.main()

0 comments on commit db4ddb9

Please sign in to comment.