Commit e01a71e0 authored by Martin Klapetek's avatar Martin Klapetek
Browse files

[ownCloud] Rewrite the wizard in QML

As it turns out, the plasma-settings app is unable to load QWidget-based
plugin UIs, so this needs to be in QML.

It's not yet perfect and should most probably be rewritten to use
StackView instead of a simple Loader, but for now it works.

The original QWidgets code is kept in place, ideally there should be a
detection of the env and show either QML (on the phone) or the QWidgets
(desktop) UI.
parent b5ba599e
......@@ -9,6 +9,7 @@ include_directories (${CMAKE_CURRENT_BUILD_DIR}
set (owncloud_plugin_kaccounts_SRCS
owncloud.cpp
qmlhelper.cpp
pages/basicinfo.cpp
pages/connecting.cpp
pages/oservices.cpp
......@@ -33,6 +34,7 @@ target_link_libraries (owncloud_plugin_kaccounts
KF5::WidgetsAddons
KF5::IconThemes
KF5::I18n
KF5::Declarative
kaccounts
${ACCOUNTSQT_LIBRARIES}
${SIGNONQT_LIBRARIES}
......@@ -42,3 +44,4 @@ target_link_libraries (owncloud_plugin_kaccounts
install (TARGETS owncloud_plugin_kaccounts
DESTINATION ${PLUGIN_INSTALL_DIR}/kaccounts/ui
)
kpackage_install_package(package org.kde.kaccounts.owncloud genericqml)
......@@ -21,9 +21,14 @@
#include "pages/basicinfo.h"
#include "pages/connecting.h"
#include "pages/oservices.h"
#include "qmlhelper.h"
#include <klocalizedstring.h>
#include <kstandardguiitem.h>
#include <KDeclarative/QmlObject>
#include <QQmlEngine>
#include <QQmlContext>
#include <QPushButton>
#include <QWizard>
......@@ -46,6 +51,25 @@ OwnCloudWizard::~OwnCloudWizard()
void OwnCloudWizard::init(KAccountsUiPlugin::UiType type)
{
if (type == KAccountsUiPlugin::NewAccountDialog) {
const QString packagePath("org.kde.kaccounts.owncloud");
m_object = new KDeclarative::QmlObject();
m_object->setTranslationDomain(packagePath);
m_object->setInitializationDelayed(true);
m_object->loadPackage(packagePath);
QmlHelper *helper = new QmlHelper(this);
connect(helper, &QmlHelper::wizardFinished, this, &OwnCloudWizard::success);
connect(helper, &QmlHelper::wizardFinished, m_object, &QObject::deleteLater);
m_object->engine()->rootContext()->setContextProperty("helper", helper);
m_object->completeInitialization();
if (!m_object->package().metadata().isValid()) {
return;
}
/*
m_wizard = new QWizard();
m_wizard->setWindowTitle(i18n("Add ownCloud Account"));
......@@ -79,7 +103,7 @@ void OwnCloudWizard::init(KAccountsUiPlugin::UiType type)
m_wizard->setOption(QWizard::NoDefaultButton, false);
connect(m_wizard, &QWizard::accepted, this, &OwnCloudWizard::done);
connect(m_wizard, &QWizard::accepted, this, &OwnCloudWizard::done);*/
Q_EMIT uiReady();
}
......@@ -94,8 +118,15 @@ void OwnCloudWizard::setProviderName(const QString &providerName)
void OwnCloudWizard::showNewAccountDialog()
{
if (m_wizard) {
m_wizard->exec();
// if (m_wizard) {
// m_wizard->exec();
// }
QWindow *window = qobject_cast<QWindow *>(m_object->rootObject());
if (window) {
window->show();
window->requestActivate();
window->setTitle(m_object->package().metadata().name());
window->setIcon(QIcon::fromTheme(m_object->package().metadata().iconName()));
}
}
......
......@@ -27,6 +27,10 @@
class QWizard;
namespace KDeclarative {
class QmlObject;
}
class OwnCloudWizard : public KAccountsUiPlugin
{
Q_OBJECT
......@@ -66,6 +70,7 @@ private:
QString m_providerName;
QHash<QString, int> m_services;
KDeclarative::QmlObject *m_object;
};
#endif //OWNCLOUD_H
/*
* Copyright 2015 (C) Martin Klapetek <mklapetek@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2 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 Library General Public License for more details
*
* You should have received a copy of the GNU Library 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.
*/
import QtQuick 2.2
import QtQuick.Layouts 1.1
import QtQuick.Controls 1.2
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.components 2.0 as PlasmaComponents
import org.kde.plasma.extras 2.0 as PlasmaExtras
ColumnLayout {
id: basicInfoLayout
property bool canContinue: helper.isServerValid && nameText.length > 0 && passwordText.length > 0
function checkServer() {
helper.checkServer(serverText.text);
}
Timer {
id: checkServerTimer
interval: 1000
repeat: false
running: false
onTriggered: {
helper.checkServer(nameText.text, passwordText.text, serverText.text);
}
}
PlasmaComponents.TextField {
id: nameText
Layout.fillWidth: true
clearButtonShown: true
placeholderText: "Username"
}
PlasmaComponents.TextField {
id: passwordText
Layout.fillWidth: true
clearButtonShown: true
placeholderText: "Password"
echoMode: TextInput.Password
}
PlasmaComponents.TextField {
id: serverText
Layout.fillWidth: true
clearButtonShown: true
placeholderText: "Server"
onTextChanged: checkServerTimer.restart();
}
PlasmaComponents.Label {
id: errorLabel
Layout.fillWidth: true
visible: text.length > 0 && !checkServerTimer.running
text: helper.errorMessage
}
Item {
Layout.fillWidth: true
Layout.fillHeight: true
PlasmaComponents.BusyIndicator {
id: busy
anchors.centerIn: parent
running: helper.isWorking
visible: running
}
PlasmaCore.IconItem {
anchors.centerIn: parent
source: "dialog-ok"
visible: !helper.isWorking && helper.isServerValid && !errorLabel.visible
}
}
}
/*
* Copyright 2015 (C) Martin Klapetek <mklapetek@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2 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 Library General Public License for more details
*
* You should have received a copy of the GNU Library 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.
*/
import QtQuick 2.2
import QtQuick.Layouts 1.1
import org.kde.plasma.components 2.0 as PlasmaComponents
import org.kde.plasma.extras 2.0 as PlasmaExtras
ColumnLayout {
id: basicInfoLayout
property bool canContinue: true
//FIXME at some point this should become a list of disabled services
property alias contactsEnabled: contactsService.checked
PlasmaExtras.Heading {
text: i18n("Choose services to enable");
}
PlasmaComponents.CheckBox {
id: contactsService
text: i18n("Contacts")
}
}
/*
* Copyright 2015 (C) Martin Klapetek <mklapetek@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2 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 Library General Public License for more details
*
* You should have received a copy of the GNU Library 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.
*/
import QtQuick 2.2
import QtQuick.Layouts 1.1
import QtQuick.Controls 1.2
import org.kde.plasma.components 2.0 as PlasmaComponents
import org.kde.plasma.extras 2.0 as PlasmaExtras
ApplicationWindow {
id: kaccountsRoot
objectName: "_root"
// id: _root
width: 500; height: 800
ColumnLayout {
anchors.fill: parent
PlasmaExtras.Title {
text: i18n("Add new ownCloud account")
}
Loader {
id: loader
Layout.fillHeight: true
Layout.fillWidth: true
}
RowLayout {
PlasmaComponents.Button {
id: backButton
Layout.fillWidth: true
text: i18n("Back");
enabled: false
onClicked: {
if (loader.source == Qt.resolvedUrl("Services.qml")) {
loader.source = "BasicInfo.qml";
backButton.enabled = false;
}
}
}
PlasmaComponents.Button {
id: nextButton
Layout.fillWidth: true
text: i18n("Next")
enabled: loader.item ? loader.item.canContinue : false
visible: loader.source == Qt.resolvedUrl("BasicInfo.qml")
onClicked: {
if (loader.source == Qt.resolvedUrl("BasicInfo.qml")) {
loader.source = "Services.qml";
backButton.enabled = true;
}
}
}
PlasmaComponents.Button {
id: finishButton
Layout.fillWidth: true
text: i18n("Finish")
visible: loader.source == Qt.resolvedUrl("Services.qml")
onClicked: {
helper.finish(loader.item.contactsEnabled ? "" : "contacts");
}
}
}
}
Component.onCompleted: {
loader.source = "BasicInfo.qml"
nextButton.enabled = true;
}
}
[Desktop Entry]
Name=OwnCloud KAccounts QML plugin
Encoding=UTF-8
Type=Service
Icon=applications-internet
X-KDE-PluginInfo-Author=Martin Klapetek
X-KDE-PluginInfo-Email=mklapetek@kde.org
X-KDE-PluginInfo-Name=owncloud_kaccounts_ui
X-KDE-PluginInfo-Version=1.0
X-KDE-PluginInfo-Website=http://kde.org
X-KDE-PluginInfo-Category=Network
X-KDE-PluginInfo-Depends=
X-KDE-PluginInfo-License=GPL
X-KDE-PluginInfo-EnabledByDefault=true
X-KDE-FormFactors=handset,tablet,mediacenter
X-Plasma-MainScript=ui/main.qml
/*************************************************************************************
* Copyright (C) 2015 by Martin Klapetek <mklapetek@kde.org> *
* *
* 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 "qmlhelper.h"
#include <KIO/Job>
#include <kio/global.h>
#include <KLocalizedString>
#include <QJsonDocument>
#include <QJsonObject>
#include <QDebug>
QmlHelper::QmlHelper(QObject *parent)
: QObject(parent),
m_isWorking(false),
m_isServerValid(false),
m_errorMessage(QString())
{
}
QmlHelper::~QmlHelper()
{
}
void QmlHelper::checkServer(const QString &username, const QString &password, const QString &path)
{
m_errorMessage.clear();
Q_EMIT errorMessageChanged();
m_username = username;
m_password = password;
QString fixedUrl;
if (!path.startsWith("http://") && !path.startsWith("https://")) {
fixedUrl.append("https://");
fixedUrl.append(path);
} else {
fixedUrl = path;
}
m_json.clear();
QUrl url(fixedUrl);
url = url.adjusted(QUrl::StripTrailingSlash);
url.setPath(url.path() + '/' + "status.php");
if (url.host().isEmpty()) {
return;
}
checkServer(url);
}
void QmlHelper::checkServer(const QUrl &url)
{
qDebug() << "Checking for ownCloud instance at" << url;
setResult(false);
setWorking(true);
KIO::TransferJob *job = KIO::get(url, KIO::NoReload, KIO::HideProgressInfo);
job->setUiDelegate(0);
connect(job, SIGNAL(data(KIO::Job*,QByteArray)), SLOT(dataReceived(KIO::Job*,QByteArray)));
connect(job, SIGNAL(finished(KJob*)), this, SLOT(fileChecked(KJob*)));
}
void QmlHelper::figureOutServer(const QUrl& url)
{
if (/*url == QLatin1String("/") ||*/ url.isEmpty()) {
setResult(false);
return;
}
m_json.clear();
qDebug() << "Received url to figure out:" << url;
// This needs 2x up cause first it just removes the status.php
// and only the second call actually moves up
QUrl urlUp = KIO::upUrl(KIO::upUrl(url));
urlUp.setPath(urlUp.path() + '/' + "status.php");
if (urlUp != url) {
checkServer(urlUp.adjusted(QUrl::NormalizePathSegments));
} else {
setResult(false);
}
}
void QmlHelper::dataReceived(KIO::Job *job, const QByteArray &data)
{
Q_UNUSED(job);
m_json.append(data);
}
void QmlHelper::fileChecked(KJob* job)
{
KIO::TransferJob *kJob = qobject_cast<KIO::TransferJob *>(job);
if (kJob->error()) {
qDebug() << job->errorString();
qDebug() << job->errorText();
figureOutServer(kJob->url());
return;
}
QJsonDocument parser = QJsonDocument::fromJson(m_json);
QJsonObject map = parser.object();
if (!map.contains("version")) {
figureOutServer(kJob->url());
qDebug() << "No json";
return;
}
m_server = kJob->url().adjusted(QUrl::RemoveFilename).toString();
qDebug() << "ownCloud appears to be running at the specified URL";
setResult(true);
}
void QmlHelper::setWorking(bool start)
{
if (start == m_isWorking) {
return;
}
m_isWorking = start;
Q_EMIT isWorkingChanged();
}
void QmlHelper::setResult(bool result)
{
// setWorking(false);
m_isServerValid = result;
if (!result) {
m_errorMessage = i18n("Unable to connect to ownCloud at the given server URL. Please check the server URL.");
setWorking(false);
} else {
m_errorMessage.clear();
qDebug() << "Server URL ok, checking auth...";
QUrl url(m_server);
url.setUserName(m_username);
url.setPassword(m_password);
url = url.adjusted(QUrl::StripTrailingSlash);
url.setPath(url.path() + '/' + "remote.php/webdav/");
qDebug() << "FinalUrL: " << url;
KIO::TransferJob *job = KIO::get(url, KIO::NoReload, KIO::HideProgressInfo);
connect(job, SIGNAL(finished(KJob*)), this, SLOT(authCheckResult(KJob*)));
job->setUiDelegate(0);
}
Q_EMIT errorMessageChanged();
}
void QmlHelper::authCheckResult(KJob *job)
{
if (job->error()) {
qDebug() << job->errorString();
qDebug() << job->errorText();
}
KIO::TransferJob *kJob = qobject_cast<KIO::TransferJob*>(job);
if (kJob->isErrorPage()) {
m_errorMessage = i18n("Unable to authenticate using the provided username and password");
} else {
m_errorMessage.clear();
}
Q_EMIT errorMessageChanged();
setWorking(false);
}
bool QmlHelper::isWorking()
{
return m_isWorking;
}
bool QmlHelper::isServerValid()
{
return m_isServerValid;
}
QString QmlHelper::errorMessage() const
{
return m_errorMessage;
}
void QmlHelper::finish(bool contactsEnabled)
{
QVariantMap data;
data.insert("server", m_server);
if (!contactsEnabled) {
data.insert("__service/owncloud-contacts", false);
}
QUrl carddavUrl(m_server);
carddavUrl.setPath(carddavUrl.path() + QString("/remote.php/carddav/addressbooks/%1").arg(m_username));
data.insert("carddavUrl", carddavUrl);
Q_EMIT wizardFinished(m_username, m_password, data);
}
#include "qmlhelper.moc"
/*************************************************************************************
* Copyright (C) 2015 by Martin Klapetek <mklapetek@kde.org> *
* *
* 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 *
*************************************************************************************/
#ifndef QMLHELPER_H
#define QMLHELPER_H
#include <QObject>
#include <KIO/AccessManager>
namespace KIO
{
class Job;
};
class KJob;
class QmlHelper : public QObject
{
Q_OBJECT
Q_PROPERTY(bool isWorking READ isWorking NOTIFY isWorkingChanged)
Q_PROPERTY(bool isServerValid READ isServerValid NOTIFY isServerValidChanged)
Q_PROPERTY(QString errorMessage READ errorMessage NOTIFY errorMessageChanged)
public:
QmlHelper(QObject *parent = 0);
~QmlHelper();
Q_INVOKABLE void checkServer(const QString &username, const QString &password, const QString &server);
Q_INVOKABLE void finish(bool contactsEnabled);
bool isWorking();
bool isServerValid();
QString errorMessage() const;