Skip to content

Commit 86389d1

Browse files
committed
[FEATURE] Project may be zipped/unzipped
1 parent 33247cc commit 86389d1

File tree

7 files changed

+300
-7
lines changed

7 files changed

+300
-7
lines changed

python/core/qgsproject.sip

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -612,6 +612,24 @@ Returns the number of registered layers.
612612
:rtype: QMap<str, QgsMapLayer *>
613613
%End
614614

615+
bool unzip( const QString &filename );
616+
%Docstring
617+
Unzip a project
618+
\param filename The project filename to unzip
619+
:return: true if unzip is well performed, false otherwise
620+
.. versionadded:: 3.0
621+
:rtype: bool
622+
%End
623+
624+
bool zip( const QString &filename );
625+
%Docstring
626+
Zip the project
627+
\param filename The zip filename
628+
:return: true if zip is well performed, false otherwise
629+
.. versionadded:: 3.0
630+
:rtype: bool
631+
%End
632+
615633

616634
QList<QgsMapLayer *> addMapLayers( const QList<QgsMapLayer *> &mapLayers /Transfer/,
617635
bool addToLegend = true);

src/core/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,7 @@ SET(QGIS_CORE_SRCS
305305
qgsxmlutils.cpp
306306
qgssettings.cpp
307307
qgsstacktrace.cpp
308+
qgsarchive.cpp
308309
qgsziputils.cpp
309310

310311
composer/qgsaddremoveitemcommand.cpp
@@ -893,6 +894,7 @@ SET(QGIS_CORE_HDRS
893894
qgsvirtuallayerdefinitionutils.h
894895
qgsmapthemecollection.h
895896
qgsxmlutils.h
897+
qgsarchive.h
896898
qgsziputils.h
897899
qgsvector.h
898900
qgslocalec.h

src/core/qgsarchive.cpp

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
/***************************************************************************
2+
qgsarchive.cpp
3+
----------------
4+
5+
begin : July 07, 2017
6+
copyright : (C) 2017 by Paul Blottiere
7+
email : paul.blottiere@oslandia.com
8+
***************************************************************************/
9+
10+
/***************************************************************************
11+
* *
12+
* This program is free software; you can redistribute it and/or modify *
13+
* it under the terms of the GNU General Public License as published by *
14+
* the Free Software Foundation; either version 2 of the License, or *
15+
* (at your option) any later version. *
16+
* *
17+
***************************************************************************/
18+
19+
#include "qgsarchive.h"
20+
#include "qgsziputils.h"
21+
#include "qgsmessagelog.h"
22+
23+
QgsArchive::QgsArchive()
24+
: mDir( new QTemporaryDir() )
25+
{
26+
}
27+
28+
QgsArchive::~QgsArchive()
29+
{
30+
}
31+
32+
QString QgsArchive::dir() const
33+
{
34+
return mDir->path();
35+
}
36+
37+
void QgsArchive::clear()
38+
{
39+
mDir.reset( new QTemporaryDir() );
40+
mFilename.clear();
41+
mFiles.clear();
42+
}
43+
44+
bool QgsArchive::zip( const QString &filename )
45+
{
46+
// create a temporary path
47+
QTemporaryFile tmpFile;
48+
tmpFile.open();
49+
tmpFile.close();
50+
51+
// zip content
52+
if ( ! QgsZipUtils::zip( tmpFile.fileName(), mFiles ) )
53+
{
54+
QString err = QObject::tr( "Unable to zip content" );
55+
QgsMessageLog::logMessage( err, QStringLiteral( "QgsArchive" ) );
56+
return false;
57+
}
58+
59+
// remove existing zip file
60+
if ( QFile::exists( filename ) )
61+
QFile::remove( filename );
62+
63+
// save zip archive
64+
if ( ! tmpFile.rename( filename ) )
65+
{
66+
QString err = QObject::tr( "Unable to save zip file '%1'" ).arg( filename );
67+
QgsMessageLog::logMessage( err, QStringLiteral( "QgsArchive" ) );
68+
return false;
69+
}
70+
71+
// keep the zip filename
72+
tmpFile.setAutoRemove( false );
73+
mFilename = filename;
74+
75+
return true;
76+
}
77+
78+
bool QgsArchive::unzip( const QString &filename )
79+
{
80+
clear();
81+
82+
QgsZipUtils::unzip( filename, mDir->path(), mFiles );
83+
mFilename = filename;
84+
85+
return ! projectFile().isEmpty();
86+
}
87+
88+
void QgsArchive::addFile( const QString &file )
89+
{
90+
mFiles.append( file );
91+
}
92+
93+
QString QgsArchive::filename() const
94+
{
95+
return mFilename;
96+
}
97+
98+
QString QgsArchive::projectFile() const
99+
{
100+
Q_FOREACH ( const QString &file, mFiles )
101+
{
102+
QFileInfo fileInfo( file );
103+
if ( "qgs" == fileInfo.suffix().toLower() )
104+
return file;
105+
}
106+
107+
return QString();
108+
}
109+
110+
QStringList QgsArchive::files() const
111+
{
112+
return mFiles;
113+
}

src/core/qgsarchive.h

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/***************************************************************************
2+
qgsarchive.h
3+
----------------
4+
5+
begin : July 07, 2017
6+
copyright : (C) 2017 by Paul Blottiere
7+
email : paul.blottiere@oslandia.com
8+
***************************************************************************/
9+
10+
/***************************************************************************
11+
* *
12+
* This program is free software; you can redistribute it and/or modify *
13+
* it under the terms of the GNU General Public License as published by *
14+
* the Free Software Foundation; either version 2 of the License, or *
15+
* (at your option) any later version. *
16+
* *
17+
***************************************************************************/
18+
19+
#ifndef QGSARCHIVE_H
20+
#define QGSARCHIVE_H
21+
22+
#include "qgis_core.h"
23+
#include <QStringList>
24+
#include <QTemporaryFile>
25+
#include <QTemporaryDir>
26+
#include <memory>
27+
28+
/**
29+
* \class QgsArchive
30+
* \ingroup core
31+
* \brief Class allowing to manage the zip/unzip actions on project
32+
* \since QGIS 3.0
33+
*/
34+
class CORE_EXPORT QgsArchive
35+
{
36+
public:
37+
38+
/**
39+
* Constructor for QgsArchive
40+
*/
41+
QgsArchive();
42+
~QgsArchive();
43+
44+
bool zip( const QString &zipFilename );
45+
46+
bool unzip( const QString &zipFilename );
47+
48+
void clear();
49+
50+
void addFile( const QString &filename );
51+
52+
QString filename() const;
53+
54+
QString projectFile() const;
55+
56+
QStringList files() const;
57+
58+
QString dir() const;
59+
60+
private:
61+
// used when unzip is performed
62+
std::unique_ptr<QTemporaryDir> mDir;
63+
64+
// content of the archive
65+
QStringList mFiles;
66+
67+
// zip filename
68+
QString mFilename;
69+
};
70+
71+
#endif

src/core/qgsproject.cpp

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,7 @@ QgsProject::QgsProject( QObject *parent )
332332
, mLayoutManager( new QgsLayoutManager( this ) )
333333
, mRootGroup( new QgsLayerTree )
334334
, mLabelingEngineSettings( new QgsLabelingEngineSettings )
335+
, mArchive( new QgsArchive() )
335336
, mAutoTransaction( false )
336337
, mEvaluateDefaultValues( false )
337338
, mDirty( false )
@@ -2064,6 +2065,81 @@ QList<QgsMapLayer *> QgsProject::mapLayersByName( const QString &layerName ) con
20642065
return mLayerStore->mapLayersByName( layerName );
20652066
}
20662067

2068+
bool QgsProject::unzip( const QString &filename )
2069+
{
2070+
clearError();
2071+
std::unique_ptr<QgsArchive> archive( new QgsArchive() );
2072+
2073+
// unzip the archive
2074+
if ( !archive->unzip( filename ) )
2075+
{
2076+
setError( tr( "Unable to unzip file '%1'" ).arg( filename ) );
2077+
return false;
2078+
}
2079+
2080+
// test if zip provides a .qgs file
2081+
if ( archive->projectFile().isEmpty() )
2082+
{
2083+
setError( tr( "Zip archive does not provide a project file" ) );
2084+
return false;
2085+
}
2086+
2087+
// read the project file
2088+
if ( ! read( archive->projectFile() ) )
2089+
{
2090+
setError( tr( "Cannot read unzipped qgs project file" ) );
2091+
return false;
2092+
}
2093+
2094+
// keep the archive
2095+
mArchive.reset( archive.release() );
2096+
2097+
return true;
2098+
}
2099+
2100+
bool QgsProject::zip( const QString &filename )
2101+
{
2102+
clearError();
2103+
2104+
// save the current project in a temporary .qgs file
2105+
QgsArchive archive;
2106+
const QString baseName = QFileInfo( filename ).baseName();
2107+
const QString qgsFileName = QString( "%1.qgs" ).arg( baseName );
2108+
QFile qgsFile( QDir( archive.dir() ).filePath( qgsFileName ) );
2109+
2110+
bool writeOk;
2111+
if ( qgsFile.open( QIODevice::WriteOnly ) )
2112+
{
2113+
const QString originalFilename = mFile.fileName();
2114+
mFile.setFileName( qgsFile.fileName() );
2115+
2116+
writeOk = write();
2117+
2118+
mFile.setFileName( originalFilename );
2119+
qgsFile.close();
2120+
}
2121+
2122+
// stop here with an error message
2123+
if ( ! writeOk )
2124+
{
2125+
setError( tr( "Unable to write temporary qgs file" ) );
2126+
return false;
2127+
}
2128+
2129+
// create the archive
2130+
archive.addFile( qgsFile.fileName() );
2131+
2132+
// zip
2133+
QString errMsg;
2134+
if ( !archive.zip( filename ) )
2135+
{
2136+
setError( tr( "Unable to perform zip" ) );
2137+
return false;
2138+
}
2139+
2140+
return true;
2141+
}
2142+
20672143
QList<QgsMapLayer *> QgsProject::addMapLayers(
20682144
const QList<QgsMapLayer *> &layers,
20692145
bool addToLegend,

src/core/qgsproject.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
#include "qgsprojectproperty.h"
4141
#include "qgsmaplayer.h"
4242
#include "qgsmaplayerstore.h"
43+
#include "qgsarchive.h"
4344

4445
class QFileInfo;
4546
class QDomDocument;
@@ -575,6 +576,22 @@ class CORE_EXPORT QgsProject : public QObject, public QgsExpressionContextGenera
575576
*/
576577
QMap<QString, QgsMapLayer *> mapLayers() const;
577578

579+
/**
580+
* Unzip a project
581+
* \param filename The project filename to unzip
582+
* \returns true if unzip is well performed, false otherwise
583+
* \since QGIS 3.0
584+
*/
585+
bool unzip( const QString &filename );
586+
587+
/**
588+
* Zip the project
589+
* \param filename The zip filename
590+
* \returns true if zip is well performed, false otherwise
591+
* \since QGIS 3.0
592+
*/
593+
bool zip( const QString &filename );
594+
578595
#ifndef SIP_RUN
579596

580597
/** Returns a list of registered map layers with a specified layer type.
@@ -1048,6 +1065,8 @@ class CORE_EXPORT QgsProject : public QObject, public QgsExpressionContextGenera
10481065

10491066
QVariantMap mCustomVariables;
10501067

1068+
std::unique_ptr<QgsArchive> mArchive;
1069+
10511070
QFile mFile; // current physical project file
10521071
mutable QgsProjectPropertyKey mProperties; // property hierarchy, TODO: this shouldn't be mutable
10531072
QString mTitle; // project title

src/core/qgsziputils.cpp

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -115,13 +115,7 @@ bool QgsZipUtils::unzip( const QString &zipFilename, const QString &dir, QString
115115

116116
bool QgsZipUtils::zip( const QString &zipFilename, const QStringList &files )
117117
{
118-
if ( QFileInfo::exists( zipFilename ) )
119-
{
120-
QString err = QObject::tr( "Error zip file yet exist: '%1'" ).arg( zipFilename );
121-
QgsMessageLog::logMessage( err, QStringLiteral( "QgsZipUtils" ) );
122-
return false;
123-
}
124-
else if ( zipFilename.isEmpty() )
118+
if ( zipFilename.isEmpty() )
125119
{
126120
QString err = QObject::tr( "Error zip filename is empty" );
127121
QgsMessageLog::logMessage( err, QStringLiteral( "QgsZipUtils" ) );

0 commit comments

Comments
 (0)