6#include "OwncloudSyncBackend.h"
10#include "MarbleDebug.h"
11#include "GeoDocument.h"
13#include "RenderPlugin.h"
15#include "RoutingModel.h"
16#include "GeoDataParser.h"
17#include "GeoDataFolder.h"
18#include "RoutingManager.h"
20#include "GeoDataDocument.h"
21#include "CloudRouteModel.h"
22#include "GeoDataPlacemark.h"
23#include "CloudSyncManager.h"
24#include "GeoDataExtendedData.h"
25#include "GeoDataData.h"
27#include <QNetworkAccessManager>
28#include <QNetworkRequest>
29#include <QJsonDocument>
39class Q_DECL_HIDDEN OwncloudSyncBackend::Private {
42 Private( CloudSyncManager* cloudSyncManager );
56 QString m_routeDownloadEndpoint;
60 CloudSyncManager* m_cloudSyncManager;
64OwncloudSyncBackend::Private::Private( CloudSyncManager* cloudSyncManager ) :
65 m_cacheDir(MarbleDirs::localPath() +
QLatin1String(
"/cloudsync/cache/routes/")),
69 m_routeDownloadReply(),
74 m_routeUploadEndpoint(
"routes/create" ),
75 m_routeListEndpoint(
"routes" ),
76 m_routeDownloadEndpoint(
"routes" ),
77 m_routeDeleteEndpoint(
"routes/delete" ),
78 m_routePreviewEndpoint(
"routes/preview" ),
79 m_cloudSyncManager( cloudSyncManager )
83OwncloudSyncBackend::OwncloudSyncBackend( CloudSyncManager* cloudSyncManager ) :
84 d( new Private( cloudSyncManager ) )
86 connect(d->m_cloudSyncManager, SIGNAL(apiUrlChanged(
QUrl)),
this, SLOT(validateSettings()));
89OwncloudSyncBackend::~OwncloudSyncBackend()
94void OwncloudSyncBackend::uploadRoute(
const QString ×tamp )
96 QString word =
"----MarbleCloudBoundary";
105 data.
append(
"Content-Disposition: form-data; name=\"timestamp\"" );
106 data.
append(
"\r\n\r\n" );
111 data.
append(
"Content-Disposition: form-data; name=\"name\"" );
112 data.
append(
"\r\n\r\n" );
113 data.
append( routeName( timestamp ).toUtf8() );
117 QFile kmlFile( d->m_cacheDir.absolutePath() +
QString(
"/%0.kml" ).arg( timestamp ) );
120 mDebug() <<
"Could not open " << timestamp <<
".kml. Either it has not been saved" <<
121 " to cache for upload or another application removed it from there.";
125 GeoDataParser parser(GeoData_KML);
126 if (!parser.read(&kmlFile)) {
127 mDebug() <<
"[OwncloudSyncBackend] KML file" << kmlFile.fileName()
128 <<
"is broken so I can't fill required properties";
132 GeoDataDocument *root =
dynamic_cast<GeoDataDocument*
>(parser.releaseDocument());
133 if (!root || root->size() < 2) {
134 mDebug() <<
"[OwncloudSyncBackend] Root document is broken";
138 GeoDataDocument *doc = geodata_cast<GeoDataDocument>(root->child(1));
139 if (!doc || doc->size() < 1) {
140 mDebug() <<
"[OwncloudSyncBackend] Tracking document is broken";
144 GeoDataPlacemark *placemark = geodata_cast<GeoDataPlacemark>(doc->child(0));
146 mDebug() <<
"[OwncloudSyncBackend] Placemark is broken";
153 mDebug() <<
"[Owncloud] Duration on write is" << duration;
154 data.
append(
"Content-Disposition: form-data; name=\"duration\"" );
155 data.
append(
"\r\n\r\n" );
162 placemark->extendedData().value(QStringLiteral(
"length")).value().toDouble();
163 mDebug() <<
"[Owncloud] Distance on write is" <<
distance;
164 data.
append(
"Content-Disposition: form-data; name=\"distance\"" );
165 data.
append(
"\r\n\r\n" );
171 data.
append(
QString(
"Content-Disposition: form-data; name=\"kml\"; filename=\"%0.kml\"" ).arg( timestamp ).toUtf8() );
173 data.
append(
"Content-Type: application/vnd.google-earth.kml+xml" );
174 data.
append(
"\r\n\r\n" );
177 data.
append( kmlFile.readAll() );
184 data.
append(
QString(
"Content-Disposition: form-data; name=\"preview\"; filename=\"%0.jpg\"" ).arg( timestamp ).toUtf8() );
186 data.
append(
"Content-Type: image/jpg" );
187 data.
append(
"\r\n\r\n" );
190 QBuffer previewBuffer( &previewBytes );
191 QPixmap preview = createPreview( timestamp );
192 preview.
save( &previewBuffer,
"JPG" );
194 data.
append( previewBytes );
198 d->m_routeUploadReply = d->m_network.post( request, data );
199 connect( d->m_routeUploadReply, SIGNAL(uploadProgress(qint64,qint64)),
this, SIGNAL(routeUploadProgress(qint64,qint64)) );
202void OwncloudSyncBackend::downloadRouteList()
205 d->m_routeListReply = d->m_network.get( request );
206 connect( d->m_routeListReply, SIGNAL(downloadProgress(qint64,qint64)),
this, SIGNAL(routeListDownloadProgress(qint64,qint64)) );
207 connect( d->m_routeListReply, SIGNAL(finished()),
this, SLOT(prepareRouteList()) );
210void OwncloudSyncBackend::downloadRoute(
const QString ×tamp )
212 QNetworkRequest routeRequest( endpointUrl( d->m_routeDownloadEndpoint, timestamp ) );
213 d->m_routeDownloadReply = d->m_network.get( routeRequest );
214 connect( d->m_routeDownloadReply, SIGNAL(finished()),
this, SLOT(saveDownloadedRoute()) );
215 connect( d->m_routeDownloadReply, SIGNAL(downloadProgress(qint64,qint64)),
this, SIGNAL(routeDownloadProgress(qint64,qint64)) );
218void OwncloudSyncBackend::deleteRoute(
const QString ×tamp )
220 QUrl url( endpointUrl( d->m_routeDeleteEndpoint, timestamp ) );
222 d->m_routeDeleteReply = d->m_network.deleteResource( request );
223 connect( d->m_routeDeleteReply, SIGNAL(finished()),
this, SIGNAL(routeDeleted()) );
226QPixmap OwncloudSyncBackend::createPreview(
const QString ×tamp )
const
228 MarbleWidget mapWidget;
229 for( RenderPlugin* plugin: mapWidget.renderPlugins() ) {
230 plugin->setEnabled(
false );
233 mapWidget.setProjection( Mercator );
234 mapWidget.setMapThemeId(QStringLiteral(
"earth/openstreetmap/openstreetmap.dgml"));
235 mapWidget.resize( 512, 512 );
237 RoutingManager* manager = mapWidget.model()->routingManager();
238 manager->loadRoute( d->m_cacheDir.absolutePath() +
QString(
"/%0.kml" ).arg( timestamp ) );
239 GeoDataLatLonBox
const bbox = manager->routingModel()->route().bounds();
241 if ( !bbox.isEmpty() ) {
242 mapWidget.centerOn( bbox );
245 QPixmap pixmap = mapWidget.grab();
246 QDir( d->m_cacheDir.absolutePath() ).
mkpath(
"preview" );
252QString OwncloudSyncBackend::routeName(
const QString ×tamp )
const
254 QFile file( d->m_cacheDir.absolutePath() +
QString(
"/%0.kml" ).arg( timestamp ) );
257 GeoDataParser parser( GeoData_KML );
258 if( !parser.read( &file ) ) {
259 mDebug() <<
"Could not read " << timestamp <<
".kml. Timestamp will be used as "
260 <<
"route name because of the problem";
266 GeoDocument *geoDoc = parser.releaseDocument();
267 GeoDataDocument *container =
dynamic_cast<GeoDataDocument*
>( geoDoc );
268 if ( container && container->size() > 0 ) {
269 GeoDataFolder *folder = container->folderList().
at( 0 );
270 for ( GeoDataPlacemark *placemark: folder->placemarkList() ) {
271 routeName.
append( placemark->name() );
272 routeName.
append(
" - " );
276 return routeName.
left( routeName.
length() - 3 );
279void OwncloudSyncBackend::validateSettings()
281 if( d->m_cloudSyncManager->owncloudServer().size() > 0
282 && d->m_cloudSyncManager->owncloudUsername().size() > 0
283 && d->m_cloudSyncManager->owncloudPassword().size() > 0 )
286 d->m_authReply = d->m_network.get( request );
287 connect( d->m_authReply, SIGNAL(finished()),
this, SLOT(checkAuthReply()) );
291 d->m_cloudSyncManager->setStatus(
"", CloudSyncManager::Success);
298 QString const status = tr(
"Server '%1' could not be reached" ).
arg( d->m_cloudSyncManager->owncloudServer() );
299 d->m_cloudSyncManager->setStatus(
status , CloudSyncManager::Error );
303void OwncloudSyncBackend::checkAuthReply()
307 if ( statusCode == 0 )
310 QString result = d->m_authReply->readAll();
316 d->m_cloudSyncManager->setStatus( tr(
"The Marble app is not installed on the ownCloud server" ), CloudSyncManager::Error);
318 d->m_cloudSyncManager->setStatus( tr(
"The server is not an ownCloud server" ), CloudSyncManager::Error);
320 }
else if (result ==
QLatin1String(
"{\"message\":\"Current user is not logged in\"}") && statusCode == 401) {
322 d->m_cloudSyncManager->setStatus( tr(
"Username or password are incorrect" ), CloudSyncManager::Error);
323 }
else if ( result.
contains(
"\"status\":\"success\"") && statusCode == 200 ) {
325 d->m_cloudSyncManager->setStatus( tr(
"Login successful" ), CloudSyncManager::Success);
329void OwncloudSyncBackend::cancelUpload()
331 d->m_routeUploadReply->abort();
334void OwncloudSyncBackend::prepareRouteList()
339 d->m_routeList.clear();
343 for (
int index = 0; index < dataArray.
size(); ++index) {
344 QJsonObject dataObject = dataArray[index].toObject();
347 route.setIdentifier(dataObject.
value(QStringLiteral(
"timestamp")).
toString());
348 route.setName(dataObject.
value(QStringLiteral(
"name")).
toString() );
349 route.setDistance(dataObject.
value(QStringLiteral(
"distance")).
toString());
350 route.setDuration(dataObject.
value(QStringLiteral(
"duration")).
toString());
351 route.setPreviewUrl( endpointUrl( d->m_routePreviewEndpoint, route.identifier() ) );
352 route.setOnCloud(
true );
354 d->m_routeList.
append( route );
359 if( !d->m_routeList.isEmpty() ) {
360 d->m_routeList.remove( d->m_routeList.count() - 1 );
363 emit routeListDownloaded( d->m_routeList );
366void OwncloudSyncBackend::saveDownloadedRoute()
370 bool pathCreated = d->m_cacheDir.mkpath( d->m_cacheDir.absolutePath() );
371 if ( !pathCreated ) {
372 mDebug() <<
"Couldn't create the path " << d->m_cacheDir.absolutePath() <<
373 ". Check if your user has sufficient permissions for this operation.";
376 QString kmlFilePath =
QString(
"%0/%1.kml").
arg( d->m_cacheDir.absolutePath(), timestamp );
377 QFile kmlFile( kmlFilePath );
381 mDebug() <<
"Failed to open file" << kmlFilePath <<
" for writing."
382 <<
" Its directory either is missing or is not writable.";
386 kmlFile.write( d->m_routeDownloadReply->readAll() );
389 QString previewPath =
QString(
"%0/preview/" ).
arg( d->m_cacheDir.absolutePath() );
390 bool previewPathCreated = d->m_cacheDir.mkpath( previewPath );
391 if ( !previewPathCreated ) {
392 mDebug() <<
"Couldn't create the path " << previewPath <<
393 ". Check if your user has sufficient permissions for this operation.";
396 QString previewFilePath =
QString(
"%0/preview/%1.jpg").
arg( d->m_cacheDir.absolutePath(), timestamp );
397 QFile previewFile( previewFilePath );
400 if ( !previewFileOpened ) {
401 mDebug() <<
"Failed to open file" << previewFilePath <<
"for writing."
402 <<
" Its directory either is missing or is not writable.";
406 QPixmap preview = createPreview( timestamp );
407 preview.
save( &previewFile,
"JPG" );
410 emit routeDownloaded();
413QUrl OwncloudSyncBackend::endpointUrl(
const QString &endpoint )
const
415 const QString endpointUrl = d->m_cloudSyncManager->apiUrl().toString() +
QLatin1Char(
'/') + endpoint;
416 return QUrl( endpointUrl );
419QUrl OwncloudSyncBackend::endpointUrl(
const QString &endpoint,
const QString ¶meter )
const
422 return QUrl( endpointUrl );
425void OwncloudSyncBackend::removeFromCache(
const QDir &cacheDir,
const QString ×tamp )
429 if ( !fileRemoved || !previewRemoved ) {
430 mDebug() <<
"Failed to remove locally cached route " << timestamp <<
". It might "
431 "have been removed already, or its directory is missing / not writable.";
434 emit removedFromCache( timestamp );
439#include "moc_OwncloudSyncBackend.cpp"
This file contains the headers for MarbleModel.
Q_SCRIPTABLE CaptureState status()
char * toString(const EngineQuery &query)
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
Binds a QML item to a specific geodetic location in screen coordinates.
KOSM_EXPORT double distance(const std::vector< const OSM::Node * > &path, Coordinate coord)
QByteArray & append(QByteArrayView data)
QString absolutePath() const const
bool mkpath(const QString &dirPath) const const
QString fileName() const const
void append(const QJsonValue &value)
qsizetype size() const const
QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error)
QJsonObject object() const const
QJsonValue value(QLatin1StringView key) const const
bool isArray() const const
QJsonArray toArray() const const
bool save(QIODevice *device, const char *format, int quality) const const
QString & append(QChar ch)
QString arg(Args &&... args) const const
const QChar at(qsizetype position) const const
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
QString left(qsizetype n) const const
qsizetype length() const const
QString number(double n, char format, int precision)
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
int toInt(bool *ok, int base) const const
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
QTime fromString(QStringView string, QStringView format)
int secsTo(QTime t) const const