Calendar Cloud Services Configuration

.. configuring calendar access was lost when
   refactoring the cloud account config.

.. we use the cloud service framework for the
   config steps (Oauth/user+pass etc).

.. existing code for interacting with the
   service in CalendarDownload.cpp and
   CalDAV.cpp is left untouched for now since
   it isn't used in many places.
This commit is contained in:
Mark Liversedge
2018-04-10 14:04:25 +01:00
parent 8f4f979d7a
commit ea8df0e3ec
9 changed files with 221 additions and 23 deletions

View File

@@ -110,14 +110,12 @@ AddClass::AddClass(AddCloudWizard *parent) : QWizardPage(parent), wizard(parent)
mapper->setMapping(p, CloudService::Measures);
layout->addWidget(p);
#if 0 // DEPRECATING (?)
// Activities
p = new QCommandLinkButton(tr("Calendar"), tr("Sync planned workouts to WebDAV and CalDAV calendars."));
// Calendar
p = new QCommandLinkButton(tr("Calendar"), tr("Sync planned workouts to WebDAV and CalDAV calendars like Google Calendar."));
p->setStyleSheet(QString("font-size: %1px;").arg(font.pointSizeF() * dpiXFactor));
connect(p, SIGNAL(clicked()), mapper, SLOT(map()));
mapper->setMapping(p, CloudService::Calendar);
layout->addWidget(p);
#endif
setFinalPage(false);
}

View File

@@ -137,7 +137,7 @@ class AddAuth : public QWizardPage
public slots:
void initializePage();
bool validatePage();
int nextId() const { return wizard->cloudService->type() & CloudService::Measures ? 90 : (hasAthlete ? 25 : 30); }
int nextId() const { return wizard->cloudService->type() & (CloudService::Measures|CloudService::Calendar) ? 90 : (hasAthlete ? 25 : 30); }
void updateServiceSettings();
void doAuth();

View File

@@ -58,13 +58,13 @@ class CalDAV : public QObject
Q_OBJECT
G_OBJECT
public:
enum action { Options, PropFind, Put, Get, Events, Report, None };
typedef enum action ActionType;
enum type { Standard, Google };
enum type { Standard, Google, Webcal };
typedef enum type CalDAVType;
public:
CalDAV(Context *context);
public slots:

83
src/Cloud/CalDAVCloud.cpp Normal file
View File

@@ -0,0 +1,83 @@
/*
* Copyright (c) 2017 Mark Liversedge (liversedge@gmail.com)
* Copyright (c) 2013 Damien.Grauser (damien.grauser@pev-geneve.ch)
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "CalDAVCloud.h"
#include "Athlete.h"
#include "Settings.h"
#include "mvjson.h"
#include <QByteArray>
#include <QHttpMultiPart>
#include <QJsonDocument>
#include <QJsonArray>
#include <QJsonObject>
#include <QJsonValue>
#ifndef CALDAV_DEBUG
#define CALDAV_DEBUG false
#endif
#ifdef Q_CC_MSVC
#define printd(fmt, ...) do { \
if (CALDAV_DEBUG) { \
printf("[%s:%d %s] " fmt , __FILE__, __LINE__, \
__FUNCTION__, __VA_ARGS__); \
fflush(stdout); \
} \
} while(0)
#else
#define printd(fmt, args...) \
do { \
if (CALDAV_DEBUG) { \
printf("[%s:%d %s] " fmt , __FILE__, __LINE__, \
__FUNCTION__, ##args); \
fflush(stdout); \
} \
} while(0)
#endif
CalDAVCloud::CalDAVCloud(Context *context, CalDAV::type variant) : CloudService(context), context(context), variant(variant) {
// config
if (variant == CalDAV::Google) {
settings.insert(OAuthToken, GC_GOOGLE_CALENDAR_REFRESH_TOKEN);
} else if (variant == CalDAV::Webcal) {
settings.insert(URL, GC_WEBCAL_URL);
} else {
settings.insert(URL, GC_DVURL);
settings.insert(Username, GC_DVUSER);
settings.insert(Password, GC_DVPASS);
}
}
CalDAVCloud::~CalDAVCloud() {}
QImage CalDAVCloud::logo() const
{
return QImage(":images/services/google.png");
}
static bool addCalDAVCloud() {
CloudServiceFactory::instance().addService(new CalDAVCloud(NULL, CalDAV::Google));
CloudServiceFactory::instance().addService(new CalDAVCloud(NULL, CalDAV::Standard));
CloudServiceFactory::instance().addService(new CalDAVCloud(NULL, CalDAV::Webcal));
return true;
}
static bool add = addCalDAVCloud();

91
src/Cloud/CalDAVCloud.h Normal file
View File

@@ -0,0 +1,91 @@
/*
* Copyright (c) 2018 Mark Liversedge (liversedge@gmail.com)
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
//
// Calendars are still downloaded and managed using the old code
// See CalDAV.cpp and CalendarDownload.cpp
//
// This is a stub service to use the configuration framework provided
// by the CloudService API to make it seamless for users
//
// As part of v4 planning development we may revisit this and refactor
// this class to support open/read/write etc
//
#ifndef GC_CalDAVCloud_h
#define GC_CalDAVCloud_h
#include "CloudService.h"
#include "CalDAV.h"
class QNetworkReply;
class QNetworkAccessManager;
class CalDAVCloud : public CloudService {
Q_OBJECT
public:
int type() const { return CloudService::Calendar; }
int capabilities() const { return OAuth; }
QString id() const {
switch(variant) {
case CalDAV::Google: return "Google Calendar";
case CalDAV::Webcal: return "Web Calendar";
default:
case CalDAV::Standard: return "CalDAV Calendar";
}
}
QString uiName() const {
switch(variant) {
case CalDAV::Google: return "Google Calendar";
case CalDAV::Webcal: return "Web Calendar";
default:
case CalDAV::Standard: return "CalDAV Calendar";
}
}
QString description() const {
switch(variant) {
case CalDAV::Google: return tr("Google Calendar using CalDAV protocol and authenticate using Google Account");
case CalDAV::Webcal: return tr("Web Calendar using iCal format as a web resource");
default:
case CalDAV::Standard: return tr("Generic CalDAV Calendar such as Apple iCloud calendar");
}
}
QImage logo() const;
// we can be instantiated as a generic calDAV service or a Google
// CalDAV service. We don't have separate implementations, so at startup
// we register one of each with the cloud service factory
CalDAVCloud(Context *context, CalDAV::type variant);
CloudService *clone(Context *context) { return new CalDAVCloud(context, variant); }
~CalDAVCloud();
private:
Context *context;
CalDAV::type variant;
};
#endif

View File

@@ -46,6 +46,7 @@ OAuthDialog::OAuthDialog(Context *context, OAuthSite site, CloudService *service
if (service->id() == "Strava") site = this->site = STRAVA;
if (service->id() == "Dropbox") site = this->site = DROPBOX;
if (service->id() == "Cycling Analytics") site = this->site = CYCLING_ANALYTICS;
if (service->id() == "Google Calendar") site = this->site = GOOGLE_CALENDAR;
if (service->id() == "Google Drive") site = this->site = GOOGLE_DRIVE;
if (service->id() == "University of Kent") site = this->site = KENTUNI;
if (service->id() == "Today's Plan") site = this->site = TODAYSPLAN;
@@ -129,6 +130,14 @@ OAuthDialog::OAuthDialog(Context *context, OAuthSite site, CloudService *service
#if QT_VERSION >= 0x050000
} else if (site == GOOGLE_CALENDAR) {
// OAUTH 2.0 - Google flow for installed applications
urlstr = QString("https://accounts.google.com/o/oauth2/auth?");
urlstr.append("scope=https://www.googleapis.com/auth/calendar&");
urlstr.append("redirect_uri=urn:ietf:wg:oauth:2.0:oob&");
urlstr.append("response_type=code&");
urlstr.append("client_id=").append(GC_GOOGLE_CALENDAR_CLIENT_ID);
} else if (site == GOOGLE_DRIVE) {
const QString scope = service->getSetting(GC_GOOGLE_DRIVE_AUTH_SCOPE, "drive.appdata").toString();
@@ -194,7 +203,7 @@ OAuthDialog::OAuthDialog(Context *context, OAuthSite site, CloudService *service
//
// STEP 1: LOGIN AND AUTHORISE THE APPLICATION
//
if (site == DROPBOX || site == STRAVA || site == CYCLING_ANALYTICS || site == POLAR || site == SPORTTRACKS || site == GOOGLE_DRIVE || site == KENTUNI || site == TODAYSPLAN || site == WITHINGS) {
if (site == DROPBOX || site == STRAVA || site == CYCLING_ANALYTICS || site == POLAR || site == SPORTTRACKS || site == GOOGLE_CALENDAR || site == GOOGLE_DRIVE || site == KENTUNI || site == TODAYSPLAN || site == WITHINGS) {
url = QUrl(urlstr);
view->setUrl(url);
@@ -363,7 +372,7 @@ void
OAuthDialog::loadFinished(bool ok)
{
if (site == GOOGLE_DRIVE || site == KENTUNI) {
if (site == GOOGLE_CALENDAR || site == GOOGLE_DRIVE || site == KENTUNI) {
if (ok && url.toString().startsWith("https://accounts.google.com/o/oauth2/auth")) {
@@ -380,9 +389,15 @@ OAuthDialog::loadFinished(bool ok)
QUrl params;
#endif
QString urlstr = "https://www.googleapis.com/oauth2/v3/token?";
params.addQueryItem("client_id", GC_GOOGLE_DRIVE_CLIENT_ID);
if (site == GOOGLE_CALENDAR) {
params.addQueryItem("client_id", GC_GOOGLE_CALENDAR_CLIENT_ID);
} else if (site == GOOGLE_DRIVE || site == KENTUNI) {
params.addQueryItem("client_id", GC_GOOGLE_DRIVE_CLIENT_ID);
}
if (site == GOOGLE_DRIVE || site == KENTUNI) {
if (site == GOOGLE_CALENDAR) {
params.addQueryItem("client_secret", GC_GOOGLE_CALENDAR_CLIENT_SECRET);
} else if (site == GOOGLE_DRIVE || site == KENTUNI) {
params.addQueryItem("client_secret", GC_GOOGLE_DRIVE_CLIENT_SECRET);
}
@@ -481,7 +496,7 @@ OAuthDialog::networkRequestFinished(QNetworkReply *reply)
// if we failed to extract then we have a big problem
// google uses a refresh token so trap for them only
if (((site == GOOGLE_DRIVE || site == KENTUNI) && refresh_token == "") || access_token == "") {
if (((site == GOOGLE_CALENDAR || site == GOOGLE_DRIVE || site == KENTUNI) && refresh_token == "") || access_token == "") {
// Something failed.
// Only Google uses both refresh and access tokens.
@@ -561,6 +576,16 @@ OAuthDialog::networkRequestFinished(QNetworkReply *reply)
QMessageBox information(QMessageBox::Information, tr("Information"), info);
information.exec();
} else if (site == GOOGLE_CALENDAR) {
// remove the Google Page first
url = QUrl("http://www.goldencheetah.org");
view->setUrl(url);
appsettings->setCValue(context->athlete->cyclist, GC_GOOGLE_CALENDAR_REFRESH_TOKEN, refresh_token);
QString info = QString(tr("Google Calendar authorization was successful."));
QMessageBox information(QMessageBox::Information,
tr("Information"), info);
information.exec();
} else if (site == KENTUNI) {
service->setSetting(GC_UOK_GOOGLE_DRIVE_REFRESH_TOKEN, refresh_token);

View File

@@ -64,6 +64,7 @@ public:
STRAVA,
DROPBOX,
CYCLING_ANALYTICS,
GOOGLE_CALENDAR,
GOOGLE_DRIVE,
SPORTTRACKS,
TODAYSPLAN,

View File

@@ -49,7 +49,7 @@ ConfigDialog::ConfigDialog(QDir _home, Context *context) :
QToolBar *head = addToolBar(tr("Options"));
head->setMovable(false); // oops!
setMinimumSize(700 *dpiXFactor,650 *dpiYFactor); //changed for hidpi sizing
setMinimumSize(800 *dpiXFactor,650 *dpiYFactor); //changed for hidpi sizing
#endif
// center

View File

@@ -720,11 +720,11 @@ HEADERS += Charts/Aerolab.h Charts/AerolabWindow.h Charts/AllPlot.h Charts/AllPl
}
# cloud services
HEADERS += Cloud/BodyMeasuresDownload.h Cloud/CalendarDownload.h Cloud/CloudService.h Cloud/LocalFileStore.h \
Cloud/OAuthDialog.h Cloud/OAuthManager.h Cloud/TodaysPlanBodyMeasures.h Cloud/WithingsDownload.h \
Cloud/Strava.h Cloud/CyclingAnalytics.h Cloud/RideWithGPS.h Cloud/TrainingsTageBuch.h \
Cloud/Selfloops.h Cloud/Velohero.h Cloud/SportsPlusHealth.h Cloud/AddCloudWizard.h \
Cloud/Withings.h Cloud/HrvMeasuresDownload.h Cloud/Xert.h
HEADERS += Cloud/BodyMeasuresDownload.h Cloud/CalDAVCloud.h Cloud/CalendarDownload.h Cloud/CloudService.h \
Cloud/LocalFileStore.h Cloud/OAuthDialog.h Cloud/OAuthManager.h Cloud/TodaysPlanBodyMeasures.h \
Cloud/WithingsDownload.h Cloud/Strava.h Cloud/CyclingAnalytics.h Cloud/RideWithGPS.h \
Cloud/TrainingsTageBuch.h Cloud/Selfloops.h Cloud/Velohero.h Cloud/SportsPlusHealth.h \
Cloud/AddCloudWizard.h Cloud/Withings.h Cloud/HrvMeasuresDownload.h Cloud/Xert.h
# core data
HEADERS += Core/Athlete.h Core/Context.h Core/DataFilter.h Core/FreeSearch.h Core/GcCalendarModel.h Core/GcUpgrade.h \
@@ -813,11 +813,11 @@ SOURCES += Charts/Aerolab.cpp Charts/AerolabWindow.cpp Charts/AllPlot.cpp Charts
}
## Cloud Services / Web resources
SOURCES += Cloud/BodyMeasuresDownload.cpp Cloud/CalendarDownload.cpp Cloud/CloudService.cpp Cloud/LocalFileStore.cpp \
Cloud/OAuthDialog.cpp Cloud/OAuthManager.cpp Cloud/TodaysPlanBodyMeasures.cpp Cloud/WithingsDownload.cpp \
Cloud/Strava.cpp Cloud/CyclingAnalytics.cpp Cloud/RideWithGPS.cpp Cloud/TrainingsTageBuch.cpp \
Cloud/Selfloops.cpp Cloud/Velohero.cpp Cloud/SportsPlusHealth.cpp Cloud/AddCloudWizard.cpp \
Cloud/Withings.cpp Cloud/HrvMeasuresDownload.cpp Cloud/Xert.cpp
SOURCES += Cloud/BodyMeasuresDownload.cpp Cloud/CalDAVCloud.cpp Cloud/CalendarDownload.cpp Cloud/CloudService.cpp \
Cloud/LocalFileStore.cpp Cloud/OAuthDialog.cpp Cloud/OAuthManager.cpp Cloud/TodaysPlanBodyMeasures.cpp \
Cloud/WithingsDownload.cpp Cloud/Strava.cpp Cloud/CyclingAnalytics.cpp Cloud/RideWithGPS.cpp \
Cloud/TrainingsTageBuch.cpp Cloud/Selfloops.cpp Cloud/Velohero.cpp Cloud/SportsPlusHealth.cpp \
Cloud/AddCloudWizard.cpp Cloud/Withings.cpp Cloud/HrvMeasuresDownload.cpp Cloud/Xert.cpp
## Core Data Structures
SOURCES += Core/Athlete.cpp Core/Context.cpp Core/DataFilter.cpp Core/FreeSearch.cpp Core/GcUpgrade.cpp Core/IdleTimer.cpp \