diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index 17d91d59e..bd7ab7f0e 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -482,7 +482,7 @@ MainWindow::MainWindow(const QDir &home) connect(tweetAction, SIGNAL(triggered(bool)), this, SLOT(tweetRide())); rideMenu->addAction(tweetAction); - shareAction = new QAction(tr("Share (Strava, RideWithGPS)..."), this); + shareAction = new QAction(tr("Share (Strava, RideWithGPS, CyclingAnalytics)..."), this); connect(shareAction, SIGNAL(triggered(bool)), this, SLOT(share())); rideMenu->addAction(shareAction); #endif diff --git a/src/OAuthDialog.cpp b/src/OAuthDialog.cpp index 4e32ea304..b5a27e443 100644 --- a/src/OAuthDialog.cpp +++ b/src/OAuthDialog.cpp @@ -35,12 +35,25 @@ OAuthDialog::OAuthDialog(Context *context, OAuthSite site) : layout->setContentsMargins(2,0,2,2); setLayout(layout); - QString urlstr = QString("https://www.strava.com/oauth/authorize?"); - urlstr.append("client_id=83&"); - urlstr.append("scope=view_private,write&"); + QString urlstr = ""; + if (site == STRAVA) { + urlstr = QString("https://www.strava.com/oauth/authorize?"); + urlstr.append("client_id=").append(GC_STRAVA_CLIENT_ID).append("&"); + } + else if (site == TWITTER) { + urlstr = QString("http://api.twitter.com/oauth/request_token?"); + // TODO + } + else if (site == CYCLING_ANALYTICS) { + urlstr = QString("https://www.cyclinganalytics.com/api/auth?"); + urlstr.append("client_id=").append(GC_CYCLINGANALYTICS_CLIENT_ID).append("&"); + } urlstr.append("redirect_uri=http://www.goldencheetah.org/&"); + urlstr.append("scope=modify_rides&"); urlstr.append("response_type=code&"); urlstr.append("approval_prompt=force"); + + url = QUrl(urlstr); view = new QWebView(); @@ -63,24 +76,41 @@ void OAuthDialog::urlChanged(const QUrl &url) { qDebug() << url.toString(); - if (url.toString().startsWith("http://www.goldencheetah.org/?state=&code=")) { - QString code = url.toString().right(40); + if (url.toString().startsWith("http://www.goldencheetah.org/?state=&code=") || + url.toString().startsWith("http://www.goldencheetah.org/?code=")) { + QString code = url.toString().right(url.toString().length()-url.toString().indexOf("code=")-5); qDebug() << "code" << code; - const char *request_token_uri = "https://www.strava.com/oauth/token?"; - QByteArray data; QUrl params; + QString urlstr = ""; - params.addQueryItem("code", code); - params.addQueryItem("client_id", GC_STRAVA_CLIENT_ID); + if (site == STRAVA) { + urlstr = QString("https://www.strava.com/oauth/token?"); + params.addQueryItem("client_id", GC_STRAVA_CLIENT_ID); #ifdef GC_STRAVA_CLIENT_SECRET - params.addQueryItem("client_secret", GC_STRAVA_CLIENT_SECRET); + params.addQueryItem("client_secret", GC_STRAVA_CLIENT_SECRET); #endif - params.addQueryItem("redirect_uri", "http://www.goldencheetah.org/"); + params.addQueryItem("redirect_uri", "http://www.goldencheetah.org/"); + } + else if (site == TWITTER) { + urlstr = QString("http://api.twitter.com/oauth/token?"); + // TODO + } + else if (site == CYCLING_ANALYTICS) { + urlstr = QString("https://www.cyclinganalytics.com/api/token?"); + params.addQueryItem("client_id", GC_CYCLINGANALYTICS_CLIENT_ID); +#ifdef GC_CYCLINGANALYTICS_CLIENT_SECRET + params.addQueryItem("client_secret", GC_CYCLINGANALYTICS_CLIENT_SECRET); +#endif + params.addQueryItem("grant_type", "authorization_code"); + } + params.addQueryItem("code", code); + data = params.encodedQuery(); - QUrl url = QUrl( request_token_uri); + QUrl url = QUrl( urlstr); + qDebug() << "url" << url.toString(); QNetworkRequest request = QNetworkRequest(url); request.setHeader(QNetworkRequest::ContentTypeHeader,"application/x-www-form-urlencoded"); @@ -93,14 +123,25 @@ void OAuthDialog::loadFinished() { if (requestToken) { - int i = view->page()->mainFrame()->toHtml().indexOf("{\"access_token\":\"")+17; - int j = view->page()->mainFrame()->toHtml().indexOf("\"", i); - if (i>16 && j>-1) { - QString access_token = view->page()->mainFrame()->toHtml().mid(i,j-i); - qDebug() << "token" << access_token; - appsettings->setCValue(context->athlete->cyclist, GC_STRAVA_TOKEN, access_token); - - accept(); + int at = view->page()->mainFrame()->toHtml().indexOf("\"access_token\":")+16; + if (at>15) { + int i = view->page()->mainFrame()->toHtml().indexOf("\"", at); + int j = view->page()->mainFrame()->toHtml().indexOf("\"", i+1); + if (i>-1 && j>-1) { + qDebug() << "result" << view->page()->mainFrame()->toHtml(); + QString access_token = view->page()->mainFrame()->toHtml().mid(i+1,j-i-1); + qDebug() << "token" << access_token; + if (site == STRAVA) { + appsettings->setCValue(context->athlete->cyclist, GC_STRAVA_TOKEN, access_token); + } + else if (site == TWITTER) { + // TODO + } + else if (site == CYCLING_ANALYTICS) { + appsettings->setCValue(context->athlete->cyclist, GC_CYCLINGANALYTICS_TOKEN, access_token); + } + accept(); + } } } } diff --git a/src/OAuthDialog.h b/src/OAuthDialog.h index eb10af0ff..9af4df55b 100644 --- a/src/OAuthDialog.h +++ b/src/OAuthDialog.h @@ -33,7 +33,8 @@ class OAuthDialog : public QDialog public: typedef enum { STRAVA, - TWITTER + TWITTER, + CYCLING_ANALYTICS } OAuthSite; OAuthDialog(Context *context, OAuthSite site); diff --git a/src/Pages.cpp b/src/Pages.cpp index 17297763a..6d83b438a 100644 --- a/src/Pages.cpp +++ b/src/Pages.cpp @@ -303,7 +303,10 @@ CredentialsPage::CredentialsPage(QWidget *parent, Context *context) : QScrollAre //QLabel *struserLabel = new QLabel(tr("Username")); //QLabel *strpassLabel = new QLabel(tr("Password")); QLabel *strauthLabel = new QLabel(tr("Authorise")); - QLabel *strpinLabel = new QLabel(tr("PIN")); + + QLabel *can = new QLabel(tr("Cycling Analytics")); + can->setFont(current); + QLabel *canauthLabel = new QLabel(tr("Authorise")); QLabel *rwgps = new QLabel(tr("RideWithGPS")); rwgps->setFont(current); @@ -393,6 +396,17 @@ CredentialsPage::CredentialsPage(QWidget *parent, Context *context) : QScrollAre stravaAuthorised->setFixedHeight(16); stravaAuthorised->setFixedWidth(16); + cyclingAnalyticsAuthorise = new QPushButton("Authorise", this); +#ifndef GC_CYCLINGANALYTICS_CLIENT_SECRET + cyclingAnalyticsAuthorise->setEnabled(false); +#endif + + cyclingAnalyticsAuthorised = new QPushButton(this); + cyclingAnalyticsAuthorised->setContentsMargins(0,0,0,0); + cyclingAnalyticsAuthorised->setIcon(passwords.scaled(16,16)); + cyclingAnalyticsAuthorised->setIconSize(QSize(16,16)); + cyclingAnalyticsAuthorised->setFixedHeight(16); + cyclingAnalyticsAuthorised->setFixedWidth(16); rideWithGPSUser = new QLineEdit(this); rideWithGPSUser->setText(appsettings->cvalue(context->athlete->cyclist, GC_RWGPSUSER, "").toString()); @@ -457,27 +471,28 @@ CredentialsPage::CredentialsPage(QWidget *parent, Context *context) : QScrollAre grid->addWidget(twpinLabel, 12,0); grid->addWidget(str, 13,0); grid->addWidget(strauthLabel, 14,0); - grid->addWidget(strpinLabel, 15,0); - grid->addWidget(rwgps, 17,0); - grid->addWidget(rwgpsuserLabel, 18,0); - grid->addWidget(rwgpspassLabel, 19,0); - grid->addWidget(wip, 20,0); - grid->addWidget(wiurlLabel, 21,0); - grid->addWidget(wiuserLabel, 22,0); - grid->addWidget(wipassLabel, 23,0); - grid->addWidget(zeo, 24,0); - grid->addWidget(zeourlLabel, 25,0); - grid->addWidget(zeouserLabel, 26,0); - grid->addWidget(zeopassLabel, 27,0); - grid->addWidget(webcal, 28, 0); - grid->addWidget(wcurlLabel, 29, 0); - grid->addWidget(dv, 30,0); - grid->addWidget(dvurlLabel, 31,0); - grid->addWidget(dvuserLabel, 32,0); - grid->addWidget(dvpassLabel, 33,0); - grid->addWidget(ttb, 34,0); - grid->addWidget(ttbuserLabel, 35,0); - grid->addWidget(ttbpassLabel, 36,0); + grid->addWidget(can, 15,0); + grid->addWidget(canauthLabel, 16,0); + grid->addWidget(rwgps, 18,0); + grid->addWidget(rwgpsuserLabel, 19,0); + grid->addWidget(rwgpspassLabel, 20,0); + grid->addWidget(wip, 21,0); + grid->addWidget(wiurlLabel, 22,0); + grid->addWidget(wiuserLabel, 23,0); + grid->addWidget(wipassLabel, 24,0); + grid->addWidget(zeo, 25,0); + grid->addWidget(zeourlLabel, 26,0); + grid->addWidget(zeouserLabel, 27,0); + grid->addWidget(zeopassLabel, 28,0); + grid->addWidget(webcal, 29, 0); + grid->addWidget(wcurlLabel, 30, 0); + grid->addWidget(dv, 31,0); + grid->addWidget(dvurlLabel, 32,0); + grid->addWidget(dvuserLabel, 33,0); + grid->addWidget(dvpassLabel, 34,0); + grid->addWidget(ttb, 35,0); + grid->addWidget(ttbuserLabel, 36,0); + grid->addWidget(ttbpassLabel, 37,0); grid->addWidget(tpURL, 1, 1, 0); grid->addWidget(tpUser, 2, 1, Qt::AlignLeft | Qt::AlignVCenter); @@ -496,25 +511,29 @@ CredentialsPage::CredentialsPage(QWidget *parent, Context *context) : QScrollAre if (appsettings->cvalue(context->athlete->cyclist, GC_STRAVA_TOKEN, "")!="") grid->addWidget(stravaAuthorised, 14, 1, Qt::AlignLeft | Qt::AlignVCenter); - grid->addWidget(rideWithGPSUser, 18, 1, Qt::AlignLeft | Qt::AlignVCenter); - grid->addWidget(rideWithGPSPass, 19, 1, Qt::AlignLeft | Qt::AlignVCenter); + grid->addWidget(cyclingAnalyticsAuthorise, 16, 1, Qt::AlignLeft | Qt::AlignVCenter); + if (appsettings->cvalue(context->athlete->cyclist, GC_CYCLINGANALYTICS_TOKEN, "")!="") + grid->addWidget(cyclingAnalyticsAuthorised, 16, 1, Qt::AlignLeft | Qt::AlignVCenter); - grid->addWidget(wiURL, 21, 1, 0); - grid->addWidget(wiUser, 22, 1, Qt::AlignLeft | Qt::AlignVCenter); - grid->addWidget(wiPass, 23, 1, Qt::AlignLeft | Qt::AlignVCenter); + grid->addWidget(rideWithGPSUser, 19, 1, Qt::AlignLeft | Qt::AlignVCenter); + grid->addWidget(rideWithGPSPass, 20, 1, Qt::AlignLeft | Qt::AlignVCenter); - grid->addWidget(zeoURL, 25, 1, 0); - grid->addWidget(zeoUser, 26, 1, Qt::AlignLeft | Qt::AlignVCenter); - grid->addWidget(zeoPass, 27, 1, Qt::AlignLeft | Qt::AlignVCenter); + grid->addWidget(wiURL, 22, 1, 0); + grid->addWidget(wiUser, 23, 1, Qt::AlignLeft | Qt::AlignVCenter); + grid->addWidget(wiPass, 24, 1, Qt::AlignLeft | Qt::AlignVCenter); - grid->addWidget(webcalURL, 29, 1, 0); + grid->addWidget(zeoURL, 26, 1, 0); + grid->addWidget(zeoUser, 27, 1, Qt::AlignLeft | Qt::AlignVCenter); + grid->addWidget(zeoPass, 28, 1, Qt::AlignLeft | Qt::AlignVCenter); - grid->addWidget(dvURL, 31, 1, 0); - grid->addWidget(dvUser, 32, 1, Qt::AlignLeft | Qt::AlignVCenter); - grid->addWidget(dvPass, 33, 1, Qt::AlignLeft | Qt::AlignVCenter); + grid->addWidget(webcalURL, 30, 1, 0); - grid->addWidget(ttbUser, 35, 1, Qt::AlignLeft | Qt::AlignVCenter); - grid->addWidget(ttbPass, 36, 1, Qt::AlignLeft | Qt::AlignVCenter); + grid->addWidget(dvURL, 32, 1, 0); + grid->addWidget(dvUser, 33, 1, Qt::AlignLeft | Qt::AlignVCenter); + grid->addWidget(dvPass, 34, 1, Qt::AlignLeft | Qt::AlignVCenter); + + grid->addWidget(ttbUser, 36, 1, Qt::AlignLeft | Qt::AlignVCenter); + grid->addWidget(ttbPass, 37, 1, Qt::AlignLeft | Qt::AlignVCenter); grid->setColumnStretch(0,0); grid->setColumnStretch(1,3); @@ -527,6 +546,7 @@ CredentialsPage::CredentialsPage(QWidget *parent, Context *context) : QScrollAre connect(twitterAuthorise, SIGNAL(clicked()), this, SLOT(authoriseTwitter())); connect(stravaAuthorise, SIGNAL(clicked()), this, SLOT(authoriseStrava())); + connect(cyclingAnalyticsAuthorise, SIGNAL(clicked()), this, SLOT(authoriseCyclingAnalytics())); } @@ -603,6 +623,15 @@ void CredentialsPage::authoriseStrava() #endif } +void CredentialsPage::authoriseCyclingAnalytics() +{ +#ifdef GC_HAVE_LIBOAUTH + OAuthDialog *oauthDialog = new OAuthDialog(context, OAuthDialog::CYCLING_ANALYTICS); + oauthDialog->setWindowModality(Qt::ApplicationModal); + oauthDialog->exec(); +#endif +} + void CredentialsPage::saveClicked() { diff --git a/src/Pages.h b/src/Pages.h index 37044e435..190ebe518 100644 --- a/src/Pages.h +++ b/src/Pages.h @@ -131,6 +131,7 @@ class CredentialsPage : public QScrollArea public slots: void authoriseTwitter(); void authoriseStrava(); + void authoriseCyclingAnalytics(); private: Context *context; @@ -158,6 +159,9 @@ class CredentialsPage : public QScrollArea QLineEdit *stravaPIN; char *s_id, *s_secret; + QPushButton *cyclingAnalyticsAuthorise, *cyclingAnalyticsAuthorised; + + QLineEdit *rideWithGPSUser; QLineEdit *rideWithGPSPass; diff --git a/src/Settings.h b/src/Settings.h index 4b70451ce..bf7fbb398 100644 --- a/src/Settings.h +++ b/src/Settings.h @@ -151,9 +151,13 @@ #define GC_TWITTER_SECRET "twitter_secret" //Strava -#define GC_STRAVA_CLIENT_ID "83" //< client id +#define GC_STRAVA_CLIENT_ID "83" // client id #define GC_STRAVA_TOKEN "strava_token" +//Cycling Analytics +#define GC_CYCLINGANALYTICS_CLIENT_ID "1504958" // app id +#define GC_CYCLINGANALYTICS_TOKEN "cyclinganalytics_token" + // Tcx Smart recording #define GC_GARMIN_SMARTRECORD "garminSmartRecord" #define GC_GARMIN_HWMARK "garminHWMark" diff --git a/src/ShareDialog.cpp b/src/ShareDialog.cpp index baf13ccb9..d04cec37a 100644 --- a/src/ShareDialog.cpp +++ b/src/ShareDialog.cpp @@ -46,6 +46,7 @@ ShareDialog::ShareDialog(Context *context, RideItem *item) : // uploaders stravaUploader = new StravaUploader(context, ride, this); rideWithGpsUploader = new RideWithGpsUploader(context, ride, this); + cyclingAnalyticsUploader = new CyclingAnalyticsUploader(context, ride, this); QVBoxLayout *mainLayout = new QVBoxLayout(this); QGroupBox *groupBox1 = new QGroupBox(tr("Choose which sites you wish to share on: ")); @@ -55,10 +56,16 @@ ShareDialog::ShareDialog(Context *context, RideItem *item) : stravaChk->setEnabled(false); #endif rideWithGPSChk = new QCheckBox(tr("Ride With GPS")); + cyclingAnalyticsChk = new QCheckBox(tr("Cycling Analytics")); +#ifndef GC_STRAVA_CLIENT_SECRET + cyclingAnalyticsChk->setEnabled(false); +#endif QGridLayout *vbox1 = new QGridLayout(); vbox1->addWidget(stravaChk,0,0); vbox1->addWidget(rideWithGPSChk,0,2); + vbox1->addWidget(cyclingAnalyticsChk,0,3); + groupBox1->setLayout(vbox1); mainLayout->addWidget(groupBox1); @@ -132,7 +139,7 @@ ShareDialog::upload() { show(); - if (!stravaChk->isChecked() && !rideWithGPSChk->isChecked()) { + if (!stravaChk->isChecked() && !rideWithGPSChk->isChecked() && !cyclingAnalyticsChk->isChecked()) { QMessageBox aMsgBox; aMsgBox.setText(tr("No share site selected !")); aMsgBox.exec(); @@ -152,12 +159,20 @@ ShareDialog::upload() shareSiteCount ++; } + if (cyclingAnalyticsChk->isChecked()) { + shareSiteCount ++; + } + if (stravaChk->isChecked()) { - stravaUploader->uploadStrava(); + stravaUploader->upload(); } if (rideWithGPSChk->isChecked()) { - rideWithGpsUploader->uploadRideWithGPS(); + rideWithGpsUploader->upload(); + } + + if (cyclingAnalyticsChk->isChecked()) { + cyclingAnalyticsUploader->upload(); } } @@ -169,7 +184,7 @@ StravaUploader::StravaUploader(Context *context, RideItem *ride, ShareDialog *pa } void -StravaUploader::uploadStrava() +StravaUploader::upload() { // OAuth no more login token = appsettings->cvalue(context->athlete->cyclist, GC_STRAVA_TOKEN, "").toString(); @@ -252,9 +267,7 @@ StravaUploader::requestUploadStrava() TcxFileReader reader; - STRAVA_URL_SSL = "https://www.strava.com/api/v3/uploads" ; // The V3 API doc said "https://api.strava.com" but it is not working yet - - QUrl url = QUrl( STRAVA_URL_SSL ); + QUrl url = QUrl( "https://www.strava.com/api/v3/uploads" ); // The V3 API doc said "https://api.strava.com" but it is not working yet QNetworkRequest request = QNetworkRequest(url); //QString boundary = QString::number(qrand() * (90000000000) / (RAND_MAX + 1) + 10000000000, 16); @@ -367,7 +380,7 @@ StravaUploader::requestVerifyUpload() connect(&networkMgr, SIGNAL(finished(QNetworkReply *)), &eventLoop, SLOT(quit())); QByteArray out; - QUrl url = QUrl(STRAVA_URL_SSL + "/upload/status/"+stravaUploadId+"?token="+token); + QUrl url = QUrl("https://www.strava.com/api/v3/upload/status/"+stravaUploadId+"?token="+token); QNetworkRequest request = QNetworkRequest(url); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); @@ -440,7 +453,7 @@ RideWithGpsUploader::RideWithGpsUploader(Context *context, RideItem *ride, Share } void -RideWithGpsUploader::uploadRideWithGPS() +RideWithGpsUploader::upload() { if(rideWithGpsActivityId.length()>0) { @@ -633,5 +646,190 @@ RideWithGpsUploader::closeClicked() return; } +CyclingAnalyticsUploader::CyclingAnalyticsUploader(Context *context, RideItem *ride, ShareDialog *parent) : + context(context), ride(ride), parent(parent) +{ + cyclingAnalyticsUploadId = ride->ride()->getTag("Strava uploadId", ""); +} +void +CyclingAnalyticsUploader::upload() +{ + // OAuth no more login + token = appsettings->cvalue(context->athlete->cyclist, GC_CYCLINGANALYTICS_TOKEN, "").toString(); + if (token=="") + { + QMessageBox aMsgBox; + aMsgBox.setText(tr("Cannot login to CyclingAnalytics. Check permission")); + aMsgBox.exec(); + return; + } + + // allready shared ? + if(cyclingAnalyticsUploadId.length()>0) + { + overwrite = false; + + dialog = new QDialog(); + QVBoxLayout *layout = new QVBoxLayout; + + QVBoxLayout *layoutLabel = new QVBoxLayout(); + QLabel *label = new QLabel(); + label->setText(tr("This Ride is marked as already on CyclingAnalytics. Are you sure you want to upload it?")); + layoutLabel->addWidget(label); + + QPushButton *ok = new QPushButton(tr("OK"), dialog); + QPushButton *cancel = new QPushButton(tr("Cancel"), dialog); + QHBoxLayout *buttons = new QHBoxLayout(); + buttons->addStretch(); + buttons->addWidget(cancel); + buttons->addWidget(ok); + + connect(ok, SIGNAL(clicked()), this, SLOT(okClicked())); + connect(cancel, SIGNAL(clicked()), this, SLOT(closeClicked())); + + layout->addLayout(layoutLabel); + layout->addLayout(buttons); + + dialog->setLayout(layout); + + if (!dialog->exec()) return; + } + + requestUploadCyclingAnalytics(); + + if(!uploadSuccessful) + { + parent->progressLabel->setText("Error uploading to CyclingAnalytics"); + } + else + { + //requestVerifyUpload(); + parent->progressLabel->setText(tr("Successfully uploaded to CyclingAnalytics")); + } +} + +void +CyclingAnalyticsUploader::requestUploadCyclingAnalytics() +{ + parent->progressLabel->setText(tr("Upload ride to Strava...")); + parent->progressBar->setValue(parent->progressBar->value()+10/parent->shareSiteCount); + + QEventLoop eventLoop; + QNetworkAccessManager networkMgr; + + int year = ride->fileName.left(4).toInt(); + int month = ride->fileName.mid(5,2).toInt(); + int day = ride->fileName.mid(8,2).toInt(); + int hour = ride->fileName.mid(11,2).toInt(); + int minute = ride->fileName.mid(14,2).toInt();; + int second = ride->fileName.mid(17,2).toInt();; + + QDate rideDate = QDate(year, month, day); + QTime rideTime = QTime(hour, minute, second); + QDateTime rideDateTime = QDateTime(rideDate, rideTime); + + connect(&networkMgr, SIGNAL(finished(QNetworkReply*)), this, SLOT(requestUploadCyclingAnalyticsFinished(QNetworkReply*))); + connect(&networkMgr, SIGNAL(finished(QNetworkReply *)), &eventLoop, SLOT(quit())); + + TcxFileReader reader; + + QUrl url = QUrl( "https://www.cyclinganalytics.com/api/me/upload" ); + QNetworkRequest request = QNetworkRequest(url); + + //QString boundary = QString::number(qrand() * (90000000000) / (RAND_MAX + 1) + 10000000000, 16); + QString boundary = QVariant(qrand()).toString()+QVariant(qrand()).toString()+QVariant(qrand()).toString(); + + QByteArray file = reader.toByteArray(context, ride->ride(), parent->altitudeChk->isChecked(), parent->powerChk->isChecked(), parent->heartrateChk->isChecked(), parent->cadenceChk->isChecked()); + + // MULTIPART ***************** + + QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType); + multiPart->setBoundary(boundary.toAscii()); + + request.setRawHeader("Authorization", (QString("Bearer %1").arg(token)).toAscii()); + + QHttpPart activityNamePart; + activityNamePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"title\"")); + activityNamePart.setBody(QString(parent->titleEdit->text()).toAscii()); + + QHttpPart dataTypePart; + dataTypePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"format\"")); + dataTypePart.setBody("tcx"); + + QHttpPart filenamePart; + filenamePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"filename\"")); + filenamePart.setBody("file.tcx"); + + QHttpPart filePart; + filePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"data\"; filename=\"file.tcx\"; type=\"text/xml\"")); + filePart.setBody(file); + + multiPart->append(activityNamePart); + multiPart->append(filenamePart); + multiPart->append(dataTypePart); + multiPart->append(filePart); + + QScopedPointer reply( networkMgr.post(request, multiPart) ); + multiPart->setParent(reply.data()); + + parent->progressBar->setValue(parent->progressBar->value()+30/parent->shareSiteCount); + parent->progressLabel->setText(tr("Upload ride... Sending to CyclingAnalytics")); + + eventLoop.exec(); +} + +void +CyclingAnalyticsUploader::requestUploadCyclingAnalyticsFinished(QNetworkReply *reply) +{ + parent->progressBar->setValue(parent->progressBar->value()+50/parent->shareSiteCount); + parent->progressLabel->setText(tr("Upload to CyclingAnalytics finished.")); + + uploadSuccessful = false; + + QString response = reply->readAll(); + //qDebug() << "response" << response; + + QScriptValue sc; + QScriptEngine se; + + sc = se.evaluate("("+response+")"); + QString uploadError = sc.property("error").toString(); + if (uploadError.toLower() == "none" || uploadError.toLower() == "null") + uploadError = ""; + + if (uploadError.length()>0 || reply->error() != QNetworkReply::NoError) + { + //qDebug() << "Error " << reply->error() ; + //qDebug() << "Error " << uploadError; + parent->errorLabel->setText(parent->errorLabel->text()+ tr(" Error from CyclingAnalytics: ") + uploadError + "\n" ); + } + else + { + cyclingAnalyticsUploadId = sc.property("upload_id").toString(); + + ride->ride()->setTag("CyclingAnalytics uploadId", cyclingAnalyticsUploadId); + ride->setDirty(true); + + //qDebug() << "uploadId: " << cyclingAnalyticsUploadId; + + parent->progressBar->setValue(parent->progressBar->value()+10/parent->shareSiteCount); + uploadSuccessful = true; + } + //qDebug() << reply->readAll(); +} + +void +CyclingAnalyticsUploader::okClicked() +{ + dialog->accept(); + return; +} + +void +CyclingAnalyticsUploader::closeClicked() +{ + dialog->reject(); + return; +} diff --git a/src/ShareDialog.h b/src/ShareDialog.h index b8778d4e6..4605d6a3a 100644 --- a/src/ShareDialog.h +++ b/src/ShareDialog.h @@ -44,7 +44,7 @@ class StravaUploader : public QObject public: StravaUploader(Context *context, RideItem *item, ShareDialog *parent = 0); - void uploadStrava(); + void upload(); private slots: void requestUploadStrava(); @@ -64,7 +64,6 @@ private: QString token; - QString STRAVA_URL_SSL; QString stravaUploadId, stravaActivityId; bool loggedIn, uploadSuccessful; @@ -83,7 +82,7 @@ class RideWithGpsUploader : public QObject public: RideWithGpsUploader(Context *context, RideItem *item, ShareDialog *parent = 0); - void uploadRideWithGPS(); + void upload(); private slots: @@ -105,6 +104,41 @@ private: bool overwrite; }; +// uploader to strava.com +class CyclingAnalyticsUploader : public QObject +{ + Q_OBJECT + G_OBJECT + +public: + CyclingAnalyticsUploader(Context *context, RideItem *item, ShareDialog *parent = 0); + + void upload(); + +private slots: + void requestUploadCyclingAnalytics(); + void requestUploadCyclingAnalyticsFinished(QNetworkReply *reply); + + void okClicked(); + void closeClicked(); + +private: + Context *context; + ShareDialog *parent; + RideItem *ride; + QDialog *dialog; + + QString token; + + bool loggedIn, uploadSuccessful; + bool overwrite; + + QString cyclingAnalyticsUploadId; + + QString uploadStatus; + QString uploadProgress; +}; + class ShareDialog : public QDialog { Q_OBJECT @@ -139,11 +173,13 @@ private: QCheckBox *stravaChk; QCheckBox *rideWithGPSChk; + QCheckBox *cyclingAnalyticsChk; RideItem *ride; StravaUploader *stravaUploader; RideWithGpsUploader *rideWithGpsUploader; + CyclingAnalyticsUploader *cyclingAnalyticsUploader; QString athleteId; }; diff --git a/src/main.cpp b/src/main.cpp index 5b8a29daf..1bf13d606 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -36,6 +36,21 @@ main(int argc, char *argv[]) XInitThreads(); #endif +#ifdef Q_OS_MACX + if ( QSysInfo::MacintoshVersion > QSysInfo::MV_10_8 ) + { + // fix Mac OS X 10.9 (mavericks) font issue + // https://bugreports.qt-project.org/browse/QTBUG-32789 + qDebug() << "insertSubstitution .Lucida Grande UI for Lucida Grande"; + //QStringList list; + //list.append("Lucida Grande"); + //list.append("LucidaGrande"); + //QFont::insertSubstitutions(".Lucida Grande UI", list); + //QFont::insertSubstitution(".Lucida Grande UI", "Lucida Grande"); + QFont::insertSubstitution("LucidaGrande", "Lucida Grande"); + } +#endif + application = new QApplication(argc, argv); QFont font;