Add settings page for Ubuntu One.

This commit is contained in:
John Maguire 2012-11-27 19:56:47 +01:00
parent 24ac9adbe5
commit 1d20ac4eae
15 changed files with 342 additions and 50 deletions

View File

@ -294,6 +294,7 @@
<file>providers/somafm.png</file>
<file>providers/songkick.png</file>
<file>providers/soundcloud.png</file>
<file>providers/ubuntuone.png</file>
<file>providers/wikipedia.png</file>
<file>sample.mood</file>
<file>schema/device-schema.sql</file>

0
data/providers/podcast16.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 764 B

After

Width:  |  Height:  |  Size: 764 B

0
data/providers/podcast32.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -192,6 +192,7 @@ set(SOURCES
internet/soundcloudservice.cpp
internet/ubuntuoneauthenticator.cpp
internet/ubuntuoneservice.cpp
internet/ubuntuonesettingspage.cpp
internet/ubuntuoneurlhandler.cpp
library/groupbydialog.cpp
@ -469,6 +470,7 @@ set(HEADERS
internet/soundcloudservice.h
internet/ubuntuoneauthenticator.h
internet/ubuntuoneservice.h
internet/ubuntuonesettingspage.h
internet/ubuntuoneurlhandler.h
library/groupbydialog.h
@ -645,6 +647,7 @@ set(UI
internet/magnatunesettingspage.ui
internet/searchboxwidget.ui
internet/spotifysettingspage.ui
internet/ubuntuonesettingspage.ui
library/groupbydialog.ui
library/libraryfilterwidget.ui

View File

@ -1,16 +1,16 @@
/* This file is part of Clementine.
Copyright 2012, David Sansome <me@davidsansome.com>
Clementine 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 3 of the License, or
(at your option) any later version.
Clementine 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 Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
@ -26,11 +26,9 @@
class GoogleDriveService;
class Ui_GoogleDriveSettingsPage;
class QSortFilterProxyModel;
class GoogleDriveSettingsPage : public SettingsPage {
Q_OBJECT
public:
GoogleDriveSettingsPage(SettingsDialog* parent = 0);
~GoogleDriveSettingsPage();

View File

@ -1,18 +1,24 @@
#include "ubuntuoneauthenticator.h"
#include <QCoreApplication>
#include <QDateTime>
#include <QHostInfo>
#include <QStringList>
#include <qjson/parser.h>
#include "core/closure.h"
#include "core/logging.h"
#include "core/network.h"
#include "core/timeconstants.h"
namespace {
static const char* kUbuntuOneEndpoint =
"https://login.ubuntu.com/api/1.0/authentications";
static const char* kTokenNameTemplate = "Ubuntu One @ %1 [%2]";
static const char* kOAuthSSOFinishedEndpoint =
"https://one.ubuntu.com/oauth/sso-finished-so-get-tokens/";
static const char* kOAuthHeaderPrefix = "OAuth realm=\"\", ";
}
UbuntuOneAuthenticator::UbuntuOneAuthenticator(QObject* parent)
@ -66,5 +72,67 @@ void UbuntuOneAuthenticator::AuthorisationFinished(QNetworkReply* reply) {
token_ = auth_info["token"].toString();
token_secret_ = auth_info["token_secret"].toString();
CopySSOTokens();
}
QByteArray UbuntuOneAuthenticator::GenerateAuthorisationHeader(
const QString& consumer_key,
const QString& consumer_secret,
const QString& token,
const QString& token_secret) {
typedef QPair<QString, QString> Param;
QString timestamp = QString::number(
QDateTime::currentMSecsSinceEpoch() / kMsecPerSec);
QList<Param> parameters;
parameters << Param("oauth_nonce", QString::number(qrand()))
<< Param("oauth_timestamp", timestamp)
<< Param("oauth_version", "1.0")
<< Param("oauth_consumer_key", consumer_key)
<< Param("oauth_token", token)
<< Param("oauth_signature_method", "PLAINTEXT");
qSort(parameters.begin(), parameters.end());
QStringList encoded_params;
for (const Param& p : parameters) {
encoded_params << QString("%1=%2").arg(p.first, p.second);
}
QString signing_key =
consumer_secret + "&" + token_secret;
QByteArray signature = QUrl::toPercentEncoding(signing_key);
// Construct authorisation header
parameters << Param("oauth_signature", signature);
QStringList header_params;
for (const Param& p : parameters) {
header_params << QString("%1=\"%2\"").arg(p.first, p.second);
}
QString authorisation_header = header_params.join(", ");
authorisation_header.prepend(kOAuthHeaderPrefix);
return authorisation_header.toAscii();
}
QByteArray UbuntuOneAuthenticator::GenerateAuthorisationHeader() {
return GenerateAuthorisationHeader(
consumer_key_,
consumer_secret_,
token_,
token_secret_);
}
void UbuntuOneAuthenticator::CopySSOTokens() {
QUrl url(kOAuthSSOFinishedEndpoint);
QNetworkRequest request(url);
request.setRawHeader("Authorization", GenerateAuthorisationHeader());
request.setRawHeader("Accept", "application/json");
QNetworkReply* reply = network_->get(request);
NewClosure(reply, SIGNAL(finished()),
this, SLOT(CopySSOTokensFinished(QNetworkReply*)), reply);
}
void UbuntuOneAuthenticator::CopySSOTokensFinished(QNetworkReply* reply) {
reply->deleteLater();
qLog(Debug) << reply->readAll();
emit Finished();
}

View File

@ -17,11 +17,22 @@ class UbuntuOneAuthenticator : public QObject {
QString token() const { return token_; }
QString token_secret() const { return token_secret_; }
static QByteArray GenerateAuthorisationHeader(
const QString& consumer_key,
const QString& consumer_secret,
const QString& token,
const QString& token_secret);
signals:
void Finished();
private slots:
void AuthorisationFinished(QNetworkReply* reply);
void CopySSOTokensFinished(QNetworkReply* reply);
private:
void CopySSOTokens();
QByteArray GenerateAuthorisationHeader();
private:
NetworkAccessManager* network_;

View File

@ -1,6 +1,7 @@
#include "ubuntuoneservice.h"
#include <QDateTime>
#include <QSettings>
#include <qjson/parser.h>
@ -21,10 +22,7 @@ const char* UbuntuOneService::kSettingsGroup = "Ubuntu One";
namespace {
static const char* kFileStorageEndpoint =
"https://one.ubuntu.com/api/file_storage/v1/~/Ubuntu One/";
static const char* kOAuthSSOFinishedEndpoint =
"https://one.ubuntu.com/oauth/sso-finished-so-get-tokens/";
static const char* kContentRoot = "https://files.one.ubuntu.com";
static const char* kOAuthHeaderPrefix = "OAuth realm=\"\", ";
}
UbuntuOneService::UbuntuOneService(Application* app, InternetModel* parent)
@ -52,6 +50,18 @@ void UbuntuOneService::LazyPopulate(QStandardItem* item) {
}
void UbuntuOneService::Connect() {
QSettings s;
s.beginGroup(kSettingsGroup);
if (s.contains("consumer_key")) {
consumer_key_ = s.value("consumer_key").toString();
consumer_secret_ = s.value("consumer_secret").toString();
token_ = s.value("token").toString();
token_secret_ = s.value("token_secret").toString();
RequestFileList();
return;
}
UbuntuOneAuthenticator* authenticator = new UbuntuOneAuthenticator;
authenticator->StartAuthorisation(
"Username",
@ -62,36 +72,11 @@ void UbuntuOneService::Connect() {
}
QByteArray UbuntuOneService::GenerateAuthorisationHeader() {
typedef QPair<QString, QString> Param;
QString timestamp = QString::number(
QDateTime::currentMSecsSinceEpoch() / kMsecPerSec);
QList<Param> parameters;
parameters << Param("oauth_nonce", QString::number(qrand()))
<< Param("oauth_timestamp", timestamp)
<< Param("oauth_version", "1.0")
<< Param("oauth_consumer_key", consumer_key_)
<< Param("oauth_token", token_)
<< Param("oauth_signature_method", "PLAINTEXT");
qSort(parameters.begin(), parameters.end());
QStringList encoded_params;
for (const Param& p : parameters) {
encoded_params << QString("%1=%2").arg(p.first, p.second);
}
QString signing_key =
consumer_secret_ + "&" + token_secret_;
QByteArray signature = QUrl::toPercentEncoding(signing_key);
// Construct authorisation header
parameters << Param("oauth_signature", signature);
QStringList header_params;
for (const Param& p : parameters) {
header_params << QString("%1=\"%2\"").arg(p.first, p.second);
}
QString authorisation_header = header_params.join(", ");
authorisation_header.prepend(kOAuthHeaderPrefix);
return authorisation_header.toAscii();
return UbuntuOneAuthenticator::GenerateAuthorisationHeader(
consumer_key_,
consumer_secret_,
token_,
token_secret_);
}
void UbuntuOneService::AuthenticationFinished(
@ -103,19 +88,17 @@ void UbuntuOneService::AuthenticationFinished(
token_ = authenticator->token();
token_secret_ = authenticator->token_secret();
QUrl sso_url(kOAuthSSOFinishedEndpoint);
QNetworkRequest request(sso_url);
request.setRawHeader("Authorization", GenerateAuthorisationHeader());
request.setRawHeader("Accept", "application/json");
QSettings s;
s.beginGroup(kSettingsGroup);
s.setValue("consumer_key", consumer_key_);
s.setValue("consumer_secret", consumer_secret_);
s.setValue("token", token_);
s.setValue("token_secret", token_secret_);
qLog(Debug) << "Sending SSO copy request";
QNetworkReply* reply = network_->get(request);
NewClosure(reply, SIGNAL(finished()),
this, SLOT(SSORequestFinished(QNetworkReply*)), reply);
RequestFileList();
}
void UbuntuOneService::SSORequestFinished(QNetworkReply* reply) {
qLog(Debug) << Q_FUNC_INFO;
void UbuntuOneService::RequestFileList() {
QUrl files_url(kFileStorageEndpoint);
files_url.addQueryItem("include_children", "true");
QNetworkRequest request(files_url);

View File

@ -23,11 +23,11 @@ class UbuntuOneService : public InternetService {
private slots:
void AuthenticationFinished(UbuntuOneAuthenticator* authenticator);
void SSORequestFinished(QNetworkReply* reply);
void FileListRequestFinished(QNetworkReply* reply);
private:
void Connect();
void RequestFileList();
private:
QByteArray GenerateAuthorisationHeader();

View File

@ -0,0 +1,74 @@
#include "ubuntuonesettingspage.h"
#include "ui_ubuntuonesettingspage.h"
#include "core/application.h"
#include "core/closure.h"
#include "core/logging.h"
#include "internet/internetmodel.h"
#include "internet/ubuntuoneauthenticator.h"
#include "internet/ubuntuoneservice.h"
UbuntuOneSettingsPage::UbuntuOneSettingsPage(SettingsDialog* parent)
: SettingsPage(parent),
ui_(new Ui::UbuntuOneSettingsPage),
service_(dialog()->app()->internet_model()->Service<UbuntuOneService>()),
authenticated_(false) {
ui_->setupUi(this);
ui_->login_state->AddCredentialGroup(ui_->login_container);
connect(ui_->login_button, SIGNAL(clicked()), SLOT(LoginClicked()));
dialog()->installEventFilter(this);
}
void UbuntuOneSettingsPage::Load() {
QSettings s;
s.beginGroup(UbuntuOneService::kSettingsGroup);
const QString user_email = s.value("user_email").toString();
if (!user_email.isEmpty()) {
ui_->login_state->SetLoggedIn(LoginStateWidget::LoggedIn, user_email);
ui_->username->setText(user_email);
}
}
void UbuntuOneSettingsPage::Save() {
QSettings s;
s.beginGroup(UbuntuOneService::kSettingsGroup);
if (authenticated_) {
s.setValue("user_email", ui_->username->text());
}
}
void UbuntuOneSettingsPage::LoginClicked() {
ui_->login_button->setEnabled(false);
QString username = ui_->username->text();
QString password = ui_->password->text();
UbuntuOneAuthenticator* authenticator = new UbuntuOneAuthenticator;
authenticator->StartAuthorisation(username, password);
NewClosure(authenticator, SIGNAL(Finished()),
this, SLOT(Connected(UbuntuOneAuthenticator*)), authenticator);
NewClosure(authenticator, SIGNAL(Finished()),
service_, SLOT(AuthenticationFinished(UbuntuOneAuthenticator*)),
authenticator);
}
void UbuntuOneSettingsPage::LogoutClicked() {
}
void UbuntuOneSettingsPage::Connected(UbuntuOneAuthenticator* authenticator) {
ui_->login_state->SetLoggedIn(LoginStateWidget::LoggedIn, ui_->username->text());
authenticated_ = true;
}
bool UbuntuOneSettingsPage::eventFilter(QObject* object, QEvent* event) {
if (object == dialog() && event->type() == QEvent::Enter) {
ui_->login_button->setEnabled(true);
return false;
}
return SettingsPage::eventFilter(object, event);
}

View File

@ -0,0 +1,33 @@
#ifndef UBUNTUONESETTINGSPAGE_H
#define UBUNTUONESETTINGSPAGE_H
#include "ui/settingspage.h"
class UbuntuOneAuthenticator;
class UbuntuOneService;
class Ui_UbuntuOneSettingsPage;
class UbuntuOneSettingsPage : public SettingsPage {
Q_OBJECT
public:
UbuntuOneSettingsPage(SettingsDialog* parent = 0);
void Load();
void Save();
// QObject
bool eventFilter(QObject* object, QEvent* event);
private slots:
void LoginClicked();
void LogoutClicked();
void Connected(UbuntuOneAuthenticator* authenticator);
private:
Ui_UbuntuOneSettingsPage* ui_;
UbuntuOneService* service_;
bool authenticated_;
};
#endif // UBUNTUONESETTINGSPAGE_H

View File

@ -0,0 +1,117 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>UbuntuOneSettingsPage</class>
<widget class="QWidget" name="UbuntuOneSettingsPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>569</width>
<height>491</height>
</rect>
</property>
<property name="windowTitle">
<string>Ubuntu One</string>
</property>
<property name="windowIcon">
<iconset resource="../../data/data.qrc">
<normaloff>:/providers/ubuntuone.png</normaloff>:/providers/ubuntuone.png</iconset>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Clementine can play music that you have uploaded to Ubuntu One</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="LoginStateWidget" name="login_state" native="true"/>
</item>
<item>
<widget class="QWidget" name="login_container" native="true">
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>28</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="login_button">
<property name="text">
<string>Login</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="username">
<property name="placeholderText">
<string>Email address</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="password">
<property name="placeholderText">
<string>Password</string>
</property>
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>357</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>LoginStateWidget</class>
<extends>QWidget</extends>
<header>widgets/loginstatewidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources>
<include location="../../data/data.qrc"/>
</resources>
<connections/>
</ui>

View File

@ -37,6 +37,7 @@
#include "internet/digitallyimportedsettingspage.h"
#include "internet/groovesharksettingspage.h"
#include "internet/magnatunesettingspage.h"
#include "internet/ubuntuonesettingspage.h"
#include "library/librarysettingspage.h"
#include "playlist/playlistview.h"
#include "podcasts/podcastsettingspage.h"
@ -147,6 +148,8 @@ SettingsDialog::SettingsDialog(Application* app, BackgroundStreams* streams, QWi
AddPage(Page_GoogleDrive, new GoogleDriveSettingsPage(this), providers);
#endif
AddPage(Page_UbuntuOne, new UbuntuOneSettingsPage(this), providers);
#ifdef HAVE_SPOTIFY
AddPage(Page_Spotify, new SpotifySettingsPage(this), providers);
#endif

View File

@ -77,6 +77,7 @@ public:
Page_Wiimotedev,
Page_Podcasts,
Page_GoogleDrive,
Page_UbuntuOne,
};
enum Role {