Further implementing NextcloudApi clann

This commit is contained in:
Scharel Clemens 2021-02-01 08:09:42 +01:00
parent 538bc6c54f
commit c68ec1ee57
9 changed files with 368 additions and 130 deletions

View file

@ -19,13 +19,11 @@ DEFINES += APP_VERSION=\\\"$$VERSION\\\"
HEADERS += src/note.h \ HEADERS += src/note.h \
src/accounthash.h \ src/accounthash.h \
src/nextcloudapi.h \ src/nextcloudapi.h \
src/notesapi.h \
src/notesmodel.h src/notesmodel.h
SOURCES += src/harbour-nextcloudnotes.cpp \ SOURCES += src/harbour-nextcloudnotes.cpp \
src/nextcloudapi.cpp \ src/nextcloudapi.cpp \
src/note.cpp \ src/note.cpp \
src/notesapi.cpp \
src/notesmodel.cpp src/notesmodel.cpp
DISTFILES += qml/harbour-nextcloudnotes.qml \ DISTFILES += qml/harbour-nextcloudnotes.qml \

View file

@ -2,6 +2,7 @@ import QtQuick 2.2
import Sailfish.Silica 1.0 import Sailfish.Silica 1.0
import Nemo.Configuration 1.0 import Nemo.Configuration 1.0
import Nemo.Notifications 1.0 import Nemo.Notifications 1.0
import NextcloudApi 1.0
import "pages" import "pages"
ApplicationWindow ApplicationWindow
@ -136,32 +137,11 @@ ApplicationWindow
} }
} }
Connections { Nextcloud {
target: notesApi id: nextcloud
server: account.url
onNetworkAccessibleChanged: { username: account.username
console.log("Device is " + (accessible ? "online" : "offline")) password: account.passowrd
if (accessible) {
offlineNotification.close(Notification.Closed)
notesApi.getAllNotes()
}
else {
offlineNotification.publish()
}
}
onNoteError: {
apiErrorNotification.close()
if (error)
console.log("Notes API error (" + error + "): " + notesApi.errorMessage(error))
if (error && notesApi.networkAccessible) {
apiErrorNotification.body = notesApi.errorMessage(error)
apiErrorNotification.publish()
}
}
onLastSyncChanged: {
console.log("Last API sync: " + lastSync)
account.update = lastSync
}
} }
Component.onCompleted: { Component.onCompleted: {

View file

@ -3,8 +3,7 @@
#include <QtQml> #include <QtQml>
#include <QObject> #include <QObject>
#include "accounthash.h" #include "accounthash.h"
#include "note.h" #include "nextcloudapi.h"
#include "notesapi.h"
#include "notesmodel.h" #include "notesmodel.h"
int main(int argc, char *argv[]) int main(int argc, char *argv[])
@ -19,19 +18,7 @@ int main(int argc, char *argv[])
qDebug() << app->applicationDisplayName() << app->applicationVersion(); qDebug() << app->applicationDisplayName() << app->applicationVersion();
AccountHash* accountHash = new AccountHash; AccountHash* accountHash = new AccountHash;
qRegisterMetaType<Note>(); qmlRegisterType<NextcloudApi>("NextcloudApi", 1, 0, "Nextcloud");
NotesModel* notesModel = new NotesModel;
NotesProxyModel* notesProxyModel = new NotesProxyModel;
notesProxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
notesProxyModel->setSortLocaleAware(true);
notesProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
notesProxyModel->setFilterRole(NotesModel::ContentRole);
notesProxyModel->setSourceModel(notesModel);
NotesApi* notesApi = new NotesApi;
notesModel->setNotesApi(notesApi);
qmlRegisterType<NotesApi>("NextcloudNotes", 1, 0, "NotesApi");
QQuickView* view = SailfishApp::createView(); QQuickView* view = SailfishApp::createView();
#ifdef QT_DEBUG #ifdef QT_DEBUG
@ -40,17 +27,11 @@ int main(int argc, char *argv[])
view->rootContext()->setContextProperty("debug", QVariant(false)); view->rootContext()->setContextProperty("debug", QVariant(false));
#endif #endif
view->rootContext()->setContextProperty("accountHash", accountHash); view->rootContext()->setContextProperty("accountHash", accountHash);
view->rootContext()->setContextProperty("notesModel", notesModel);
view->rootContext()->setContextProperty("notesProxyModel", notesProxyModel);
view->rootContext()->setContextProperty("notesApi", notesApi);
view->setSource(SailfishApp::pathTo("qml/harbour-nextcloudnotes.qml")); view->setSource(SailfishApp::pathTo("qml/harbour-nextcloudnotes.qml"));
view->show(); view->show();
int retval = app->exec(); int retval = app->exec();
notesApi->deleteLater();
notesProxyModel->deleteLater();
notesModel->deleteLater();
return retval; return retval;
} }

View file

@ -1,17 +1,20 @@
#include "nextcloudapi.h" #include "nextcloudapi.h"
#include <QGuiApplication> #include <QGuiApplication>
#include <QAuthenticator>
#include <QJsonDocument>
NextcloudApi::NextcloudApi(QObject *parent) : QObject(parent) NextcloudApi::NextcloudApi(QObject *parent) : QObject(parent)
{ {
// Initial status // Initial status
setStatus(NextcloudStatus::NextcloudUnknown); setStatus(ApiCallStatus::ApiUnknown);
setCababilitiesStatus(CapabilitiesStatus::CapabilitiesUnknown);
setLoginStatus(LoginStatus::LoginUnknown); setLoginStatus(LoginStatus::LoginUnknown);
setCababilitiesStatus(ApiCallStatus::ApiUnknown);
setUserListStatus(ApiCallStatus::ApiUnknown);
setUserMetaStatus(ApiCallStatus::ApiUnknown);
m_status_installed = false; m_status_installed = false;
m_status_maintenance = false; m_status_maintenance = false;
m_status_needsDbUpgrade = false; m_status_needsDbUpgrade = false;
m_status_extendedSupport = false; m_status_extendedSupport = false;
m_running_requests = 0;
// Login Flow V2 poll timer // Login Flow V2 poll timer
m_loginPollTimer.setInterval(LOGIN_FLOWV2_POLL_INTERVALL); m_loginPollTimer.setInterval(LOGIN_FLOWV2_POLL_INTERVALL);
@ -37,6 +40,12 @@ NextcloudApi::NextcloudApi(QObject *parent) : QObject(parent)
} }
NextcloudApi::~NextcloudApi() { NextcloudApi::~NextcloudApi() {
while (!m_replies.empty()) {
QNetworkReply* reply = m_replies.first();
reply->abort();
reply->deleteLater();
m_replies.removeFirst();
}
} }
void NextcloudApi::setVerifySsl(bool verify) { void NextcloudApi::setVerifySsl(bool verify) {
@ -175,8 +184,7 @@ bool NextcloudApi::get(const QString& endpoint, bool authenticated) {
if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) { if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) {
QNetworkRequest request = authenticated ? m_authenticatedRequest : m_request; QNetworkRequest request = authenticated ? m_authenticatedRequest : m_request;
request.setUrl(url); request.setUrl(url);
m_manager.get(request); m_replies << m_manager.get(request);
m_running_requests ++;
return true; return true;
} }
else { else {
@ -185,24 +193,6 @@ bool NextcloudApi::get(const QString& endpoint, bool authenticated) {
return false; return false;
} }
bool NextcloudApi::post(const QString& endpoint, const QByteArray& data, bool authenticated) {
QUrl url = server();
url.setPath(url.path() + endpoint);
qDebug() << "POST" << url.toDisplayString();
qDebug() << data;
if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) {
QNetworkRequest request = authenticated ? m_authenticatedRequest : m_request;
request.setUrl(url);
m_manager.post(request, data);
m_running_requests ++;
return true;
}
else {
qDebug() << "POST URL not valid" << url.toDisplayString();
}
return false;
}
bool NextcloudApi::put(const QString& endpoint, const QByteArray& data, bool authenticated) { bool NextcloudApi::put(const QString& endpoint, const QByteArray& data, bool authenticated) {
QUrl url = server(); QUrl url = server();
url.setPath(url.path() + endpoint); url.setPath(url.path() + endpoint);
@ -211,8 +201,7 @@ bool NextcloudApi::put(const QString& endpoint, const QByteArray& data, bool aut
if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) { if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) {
QNetworkRequest request = authenticated ? m_authenticatedRequest : m_request; QNetworkRequest request = authenticated ? m_authenticatedRequest : m_request;
request.setUrl(url); request.setUrl(url);
m_manager.put(request, data); m_replies << m_manager.put(request, data);
m_running_requests ++;
return true; return true;
} }
else { else {
@ -221,6 +210,23 @@ bool NextcloudApi::put(const QString& endpoint, const QByteArray& data, bool aut
return false; return false;
} }
bool NextcloudApi::post(const QString& endpoint, const QByteArray& data, bool authenticated) {
QUrl url = server();
url.setPath(url.path() + endpoint);
qDebug() << "POST" << url.toDisplayString();
qDebug() << data;
if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) {
QNetworkRequest request = authenticated ? m_authenticatedRequest : m_request;
request.setUrl(url);
m_replies << m_manager.post(request, data);
return true;
}
else {
qDebug() << "POST URL not valid" << url.toDisplayString();
}
return false;
}
bool NextcloudApi::del(const QString& endpoint, bool authenticated) { bool NextcloudApi::del(const QString& endpoint, bool authenticated) {
QUrl url = server(); QUrl url = server();
url.setPath(url.path() + endpoint); url.setPath(url.path() + endpoint);
@ -228,8 +234,7 @@ bool NextcloudApi::del(const QString& endpoint, bool authenticated) {
if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) { if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) {
QNetworkRequest request = authenticated ? m_authenticatedRequest : m_request; QNetworkRequest request = authenticated ? m_authenticatedRequest : m_request;
request.setUrl(url); request.setUrl(url);
m_manager.deleteResource(request); m_replies << m_manager.deleteResource(request);
m_running_requests ++;
return true; return true;
} }
else { else {
@ -240,11 +245,11 @@ bool NextcloudApi::del(const QString& endpoint, bool authenticated) {
bool NextcloudApi::getStatus() { bool NextcloudApi::getStatus() {
if (get(STATUS_ENDPOINT, false)) { if (get(STATUS_ENDPOINT, false)) {
setStatus(NextcloudStatus::NextcloudBusy); setStatus(ApiCallStatus::ApiBusy);
return true; return true;
} }
else { else {
setStatus(NextcloudStatus::NextcloudFailed); setStatus(ApiCallStatus::ApiFailed);
} }
return false; return false;
} }
@ -284,6 +289,28 @@ bool NextcloudApi::deleteAppPassword() {
return del(DEL_APPPASSWORD_ENDPOINT, true); return del(DEL_APPPASSWORD_ENDPOINT, true);
} }
void NextcloudApi::verifyUrl(QUrl url) {
emit urlValidChanged(
url.isValid()&&
!url.isRelative() &&
!url.userName().isEmpty() &&
!url.password().isEmpty() &&
!url.host().isEmpty());
}
void NextcloudApi::requireAuthentication(QNetworkReply *reply, QAuthenticator *authenticator) {
if (reply && authenticator) {
authenticator->setUser(username());
authenticator->setPassword(password());
}
else
emit apiError(AuthenticationError);
}
void NextcloudApi::onNetworkAccessibleChanged(QNetworkAccessManager::NetworkAccessibility accessible) {
emit networkAccessibleChanged(accessible == QNetworkAccessManager::Accessible);
}
bool NextcloudApi::pollLoginUrl() { bool NextcloudApi::pollLoginUrl() {
if (post(m_pollUrl.path(), QByteArray("token=").append(m_pollToken), false)) { if (post(m_pollUrl.path(), QByteArray("token=").append(m_pollToken), false)) {
setLoginStatus(LoginStatus::LoginFlowV2Polling); setLoginStatus(LoginStatus::LoginFlowV2Polling);
@ -295,3 +322,153 @@ bool NextcloudApi::pollLoginUrl() {
} }
return false; return false;
} }
void NextcloudApi::sslError(QNetworkReply *reply, const QList<QSslError> &errors) {
qDebug() << "SSL errors accured while calling" << reply->url().toDisplayString();
for (int i = 0; i < errors.size(); ++i) {
qDebug() << errors[i].errorString();
}
emit apiError(SslHandshakeError);
}
void NextcloudApi::replyFinished(QNetworkReply* reply) {
if (reply->error() != QNetworkReply::NoError)
qDebug() << reply->error() << reply->errorString();
QByteArray data = reply->readAll();
QJsonDocument json = QJsonDocument::fromJson(data);
qDebug() << data;
switch (reply->error()) {
case QNetworkReply::NoError:
emit apiError(NoError);
switch (reply->operation()) {
case QNetworkAccessManager::GetOperation:
if (reply->url().toString().endsWith(STATUS_ENDPOINT)) {
updateStatus(json.object());
}
else if (reply->url().toString().endsWith(GET_APPPASSWORD_ENDPOINT)) {
updateAppPassword(json.object());
}
else if (reply->url().toString().endsWith(LIST_USERS_ENDPOINT)) {
updateUserList(json.object());
}
else if (reply->url().toString().contains(USER_METADATA_ENDPOINT)) {
updateUserMeta(json.object());
}
else if (reply->url().toString().endsWith(CAPABILITIES_ENDPOINT)) {
updateCapabilities(json.object());
}
else {
emit getFinished(reply);
break;
}
m_replies.removeOne(reply);
reply->deleteLater();
break;
case QNetworkAccessManager::PutOperation:
if (false) {
}
else {
emit putFinished(reply);
break;
}
m_replies.removeOne(reply);
reply->deleteLater();
break;
case QNetworkAccessManager::PostOperation:
if (reply->url().toString().endsWith(LOGIN_FLOWV2_ENDPOINT)) {
}
else if (reply->url() == m_pollUrl) {
}
else {
emit postFinished(reply);
break;
}
m_replies.removeOne(reply);
reply->deleteLater();
break;
case QNetworkAccessManager::DeleteOperation:
if (reply->url().toString().endsWith(DEL_APPPASSWORD_ENDPOINT)) {
}
else {
emit delFinished(reply);
break;
}
m_replies.removeOne(reply);
reply->deleteLater();
break;
default:
qDebug() << "Unknown operation" << reply->operation() << reply->url();
m_replies.removeOne(reply);
reply->deleteLater();
break;
}
break;
case QNetworkReply::AuthenticationRequiredError:
emit apiError(AuthenticationError);
break;
case QNetworkReply::ContentNotFoundError:
if (reply->url() == m_pollUrl) {
emit apiError(NoError);
}
else {
emit apiError(CommunicationError);
}
break;
default:
emit apiError(CommunicationError);
break;
}
}
bool NextcloudApi::updateStatus(const QJsonObject &status) {
}
void NextcloudApi::setStatus(ApiCallStatus status, bool *changed) {
}
bool NextcloudApi::updateLoginFlow(const QJsonObject &login) {
}
bool NextcloudApi::updateLoginCredentials(const QJsonObject &credentials) {
}
bool NextcloudApi::updateAppPassword(const QJsonObject &password) {
}
void NextcloudApi::setLoginStatus(LoginStatus status, bool *changed) {
}
bool NextcloudApi::updateUserList(const QJsonObject &users) {
}
void NextcloudApi::setUserListStatus(ApiCallStatus status, bool *changed) {
}
bool NextcloudApi::updateUserMeta(const QJsonObject &userMeta) {
}
void NextcloudApi::setUserMetaStatus(ApiCallStatus status, bool *changed) {
}
bool NextcloudApi::updateCapabilities(const QJsonObject &capabilities) {
}
void NextcloudApi::setCababilitiesStatus(ApiCallStatus status, bool *changed) {
}

View file

@ -15,9 +15,10 @@
const QString STATUS_ENDPOINT("/status.php"); const QString STATUS_ENDPOINT("/status.php");
// Capabilites and users // Capabilites and users
const QString CAPABILITIES_ENDPOINT("/ocs/v1.php/cloud/capabilities"); const QString CAPABILITIES_ENDPOINT("/ocs/v2.php/cloud/capabilities");
const QString USERS_ENDPOINT("/ocs/v1.php/cloud/users"); const QString LIST_USERS_ENDPOINT("/ocs/v2.php/cloud/users");
const QString USER_CAPABILITIES_ENDPOINT("/ocs/v1.php/cloud/users/%1"); const QString USER_METADATA_ENDPOINT("/ocs/v2.php/cloud/users/%1");
const QString USER_NOTIFICATION_ENDPOINT("/ocs/v2.php/cloud/capabilities");
// Login and authentication // Login and authentication
const QString GET_APPPASSWORD_ENDPOINT("/ocs/v2.php/core/getapppassword"); const QString GET_APPPASSWORD_ENDPOINT("/ocs/v2.php/core/getapppassword");
@ -49,7 +50,7 @@ class NextcloudApi : public QObject
Q_PROPERTY(bool busy READ busy NOTIFY busyChanged) Q_PROPERTY(bool busy READ busy NOTIFY busyChanged)
// Nextcloud status (status.php), these properties will be automatically updated on changes of the generic properties // Nextcloud status (status.php), these properties will be automatically updated on changes of the generic properties
Q_PROPERTY(NextcloudStatus statusStatus READ statusStatus NOTIFY statusStatusChanged) Q_PROPERTY(ApiCallStatus statusStatus READ statusStatus NOTIFY statusStatusChanged)
Q_PROPERTY(bool statusInstalled READ statusInstalled NOTIFY statusInstalledChanged) Q_PROPERTY(bool statusInstalled READ statusInstalled NOTIFY statusInstalledChanged)
Q_PROPERTY(bool statusMaintenance READ statusMaintenance NOTIFY statusMaintenanceChanged) Q_PROPERTY(bool statusMaintenance READ statusMaintenance NOTIFY statusMaintenanceChanged)
Q_PROPERTY(bool statusNeedsDbUpgrade READ statusNeedsDbUpgrade NOTIFY statusNeedsDbUpgradeChanged) Q_PROPERTY(bool statusNeedsDbUpgrade READ statusNeedsDbUpgrade NOTIFY statusNeedsDbUpgradeChanged)
@ -60,32 +61,28 @@ class NextcloudApi : public QObject
Q_PROPERTY(bool statusExtendedSupport READ statusExtendedSupport NOTIFY statusExtendedSupportChanged) Q_PROPERTY(bool statusExtendedSupport READ statusExtendedSupport NOTIFY statusExtendedSupportChanged)
Q_PROPERTY(bool loginFlowV2Possible READ loginFlowV2Possible NOTIFY loginFlowV2PossibleChanged) Q_PROPERTY(bool loginFlowV2Possible READ loginFlowV2Possible NOTIFY loginFlowV2PossibleChanged)
// Nextcloud capabilities
Q_PROPERTY(CapabilitiesStatus capabilitiesStatus READ capabilitiesStatus NOTIFY capabilitiesStatusChanged)
// Login status // Login status
Q_PROPERTY(LoginStatus loginStatus READ loginStatus NOTIFY loginStatusChanged) Q_PROPERTY(ApiCallStatus loginStatus READ loginStatus NOTIFY loginStatusChanged)
// User(s) status
Q_PROPERTY(ApiCallStatus userListStatus READ userListStatus NOTIFY userListStatusChanged)
Q_PROPERTY(ApiCallStatus userMetaStatus READ userMetaStatus NOTIFY userMetaStatusChanged)
// Nextcloud capabilities
Q_PROPERTY(ApiCallStatus capabilitiesStatus READ capabilitiesStatus NOTIFY capabilitiesStatusChanged)
public: public:
explicit NextcloudApi(QObject *parent = nullptr); explicit NextcloudApi(QObject *parent = nullptr);
virtual ~NextcloudApi(); virtual ~NextcloudApi();
// Status codes // Status codes
enum NextcloudStatus { enum ApiCallStatus {
NextcloudUnknown, // Initial unknown state ApiUnknown, // Initial unknown state
NextcloudBusy, // Getting information from the nextcloud server ApiBusy, // Getting information from the nextcloud server
NextcloudSuccess, // Got information about the nextcloud server ApiSuccess, // Got information from the nextcloud server
NextcloudFailed // Error getting information from the nextcloud server, see ErrorCodes ApiFailed // Error getting information from the nextcloud server, see ErrorCodes
}; };
Q_ENUM(NextcloudStatus) Q_ENUM(ApiCallStatus)
enum CapabilitiesStatus {
CapabilitiesUnknown, // Initial unknown state
CapabilitiesBusy, // Gettin information
CapabilitiesSuccess, // Capabilities successfully read
CapabilitiesFailed // Faild to retreive capabilities
};
Q_ENUM(CapabilitiesStatus)
enum LoginStatus { enum LoginStatus {
LoginUnknown, // Inital unknown state LoginUnknown, // Inital unknown state
@ -130,10 +127,10 @@ public:
bool ready() const { return urlValid() && networkAccessible() && !busy() && statusInstalled() && !statusMaintenance() && loginStatus() == LoginSuccess; } bool ready() const { return urlValid() && networkAccessible() && !busy() && statusInstalled() && !statusMaintenance() && loginStatus() == LoginSuccess; }
bool urlValid() const { return m_url.isValid(); } bool urlValid() const { return m_url.isValid(); }
bool networkAccessible() const { return m_manager.networkAccessible() == QNetworkAccessManager::Accessible; } bool networkAccessible() const { return m_manager.networkAccessible() == QNetworkAccessManager::Accessible; }
bool busy() const { return m_running_requests > 0; } bool busy() const { return m_replies.count() > 0; }
// Nextcloud status (status.php) // Nextcloud status (status.php)
NextcloudStatus statusStatus() const { return m_statusStatus; } ApiCallStatus statusStatus() const { return m_statusStatus; }
bool statusInstalled() const { return m_status_installed; } bool statusInstalled() const { return m_status_installed; }
bool statusMaintenance() const { return m_status_maintenance; } bool statusMaintenance() const { return m_status_maintenance; }
bool statusNeedsDbUpgrade() const { return m_status_needsDbUpgrade; } bool statusNeedsDbUpgrade() const { return m_status_needsDbUpgrade; }
@ -144,12 +141,16 @@ public:
bool statusExtendedSupport() const { return m_status_extendedSupport; } bool statusExtendedSupport() const { return m_status_extendedSupport; }
bool loginFlowV2Possible() const { return QVersionNumber::fromString(statusVersion()) >= QVersionNumber(LOGIN_FLOWV2_MIN_VERSION); } bool loginFlowV2Possible() const { return QVersionNumber::fromString(statusVersion()) >= QVersionNumber(LOGIN_FLOWV2_MIN_VERSION); }
// Nextcloud capabilities
CapabilitiesStatus capabilitiesStatus() const { return m_capabilitiesStatus; }
// Login status // Login status
LoginStatus loginStatus() const { return m_loginStatus; } LoginStatus loginStatus() const { return m_loginStatus; }
// User(s) status
ApiCallStatus userListStatus() const { return m_userListStatus; }
ApiCallStatus userMetaStatus() const { return m_userMetaStatus; }
// Nextcloud capabilities
ApiCallStatus capabilitiesStatus() const { return m_capabilitiesStatus; }
enum ErrorCodes { enum ErrorCodes {
NoError, NoError,
NoConnectionError, NoConnectionError,
@ -163,8 +164,8 @@ public:
public slots: public slots:
// API helper functions // API helper functions
Q_INVOKABLE bool get(const QString& endpoint, bool authenticated = true); Q_INVOKABLE bool get(const QString& endpoint, bool authenticated = true);
Q_INVOKABLE bool post(const QString& endpoint, const QByteArray& data, bool authenticated = true);
Q_INVOKABLE bool put(const QString& endpoint, const QByteArray& data, bool authenticated = true); Q_INVOKABLE bool put(const QString& endpoint, const QByteArray& data, bool authenticated = true);
Q_INVOKABLE bool post(const QString& endpoint, const QByteArray& data, bool authenticated = true);
Q_INVOKABLE bool del(const QString& endpoint, bool authenticated = true); Q_INVOKABLE bool del(const QString& endpoint, bool authenticated = true);
// Callable functions // Callable functions
@ -174,6 +175,9 @@ public slots:
Q_INVOKABLE bool verifyLogin(); Q_INVOKABLE bool verifyLogin();
Q_INVOKABLE bool getAppPassword(); Q_INVOKABLE bool getAppPassword();
Q_INVOKABLE bool deleteAppPassword(); Q_INVOKABLE bool deleteAppPassword();
Q_INVOKABLE bool getUserList();
Q_INVOKABLE bool getUserMetaData(const QString& user = QString());
Q_INVOKABLE bool getCapabilities();
signals: signals:
// Generic API properties // Generic API properties
@ -188,15 +192,16 @@ signals:
void passwordChanged(QString password); void passwordChanged(QString password);
// Class status information // Class status information
void readyChanged(bool ready);
void urlValidChanged(bool valid); void urlValidChanged(bool valid);
void networkAccessibleChanged(bool accessible); void networkAccessibleChanged(bool accessible);
void busyChanged(bool busy); void busyChanged(bool busy);
// Nextcloud capabilities // Nextcloud capabilities
void capabilitiesStatusChanged(CapabilitiesStatus status); void capabilitiesStatusChanged(ApiCallStatus status);
// Nextcloud status (status.php) // Nextcloud status (status.php)
void statusChanged(NextcloudStatus status); void statusStatusChanged(ApiCallStatus status);
void statusInstalledChanged(bool installed); void statusInstalledChanged(bool installed);
void statusMaintenanceChanged(bool maintenance); void statusMaintenanceChanged(bool maintenance);
void statusNeedsDbUpgradeChanged(bool needsDbUpgrade); void statusNeedsDbUpgradeChanged(bool needsDbUpgrade);
@ -208,33 +213,34 @@ signals:
// Login status // Login status
void loginStatusChanged(LoginStatus status); void loginStatusChanged(LoginStatus status);
void loginFlowV2PossibleChanged(bool loginV2possible);
// API helper updates // API helper updates
void getFinished(QNetworkReply& reply); void getFinished(QNetworkReply* reply);
void postFinished(QNetworkReply& reply); void postFinished(QNetworkReply* reply);
void putFinished(QNetworkReply& reply); void putFinished(QNetworkReply* reply);
void delFinished(QNetworkReply& reply); void delFinished(QNetworkReply* reply);
void apiError(ErrorCodes error); void apiError(ErrorCodes error);
private slots: private slots:
void verifyUrl(QUrl url); void verifyUrl(QUrl url);
void requireAuthentication(QNetworkReply * reply, QAuthenticator * authenticator); void requireAuthentication(QNetworkReply * reply, QAuthenticator * authenticator);
void onNetworkAccessibleChanged(QNetworkAccessManager::NetworkAccessibility accessible); void onNetworkAccessibleChanged(QNetworkAccessManager::NetworkAccessibility accessible);
void replyFinished(QNetworkReply* reply);
bool pollLoginUrl(); bool pollLoginUrl();
void sslError(QNetworkReply* reply, const QList<QSslError> &errors); void sslError(QNetworkReply* reply, const QList<QSslError> &errors);
void replyFinished(QNetworkReply* reply);
private: private:
QUrl m_url; QUrl m_url;
QNetworkAccessManager m_manager; QNetworkAccessManager m_manager;
QVector<QNetworkReply*> m_replies;
QNetworkRequest m_request; QNetworkRequest m_request;
QNetworkRequest m_authenticatedRequest; QNetworkRequest m_authenticatedRequest;
uint m_running_requests;
// Nextcloud status.php // Nextcloud status.php
void updateStatus(const QJsonObject &status); bool updateStatus(const QJsonObject &status);
void setStatus(NextcloudStatus status, bool *changed = NULL); void setStatus(ApiCallStatus status, bool *changed = NULL);
NextcloudStatus m_statusStatus; ApiCallStatus m_statusStatus;
bool m_status_installed; bool m_status_installed;
bool m_status_maintenance; bool m_status_maintenance;
bool m_status_needsDbUpgrade; bool m_status_needsDbUpgrade;
@ -245,15 +251,28 @@ private:
bool m_status_extendedSupport; bool m_status_extendedSupport;
// Nextcloud capabilities // Nextcloud capabilities
bool updateCapabilities(const QJsonObject & capabilities); bool updateCapabilities(const QJsonObject &capabilities);
void setCababilitiesStatus(CapabilitiesStatus status, bool *changed = NULL); void setCababilitiesStatus(ApiCallStatus status, bool *changed = NULL);
CapabilitiesStatus m_capabilitiesStatus; ApiCallStatus m_capabilitiesStatus;
QJsonObject m_capabilities; QJsonObject m_capabilities;
// Nextcloud users list
bool updateUserList(const QJsonObject &users);
void setUserListStatus(ApiCallStatus status, bool *changed = NULL);
ApiCallStatus m_userListStatus;
QStringList m_userList;
// Nextcloud user metadata
bool updateUserMeta(const QJsonObject &userMeta);
void setUserMetaStatus(ApiCallStatus status, bool *changed = NULL);
ApiCallStatus m_userMetaStatus;
QHash<QString, QJsonObject> m_userMeta;
// Nextcloud Login Flow v2 // Nextcloud Login Flow v2
// https://docs.nextcloud.com/server/latest/developer_manual/client_apis/LoginFlow/index.html#login-flow-v2 // https://docs.nextcloud.com/server/latest/developer_manual/client_apis/LoginFlow/index.html#login-flow-v2
bool updateLoginFlow(const QJsonObject &login); bool updateLoginFlow(const QJsonObject &login);
bool updateLoginCredentials(const QJsonObject &credentials); bool updateLoginCredentials(const QJsonObject &credentials);
bool updateAppPassword(const QJsonObject &password);
void setLoginStatus(LoginStatus status, bool *changed = NULL); void setLoginStatus(LoginStatus status, bool *changed = NULL);
LoginStatus m_loginStatus; LoginStatus m_loginStatus;
QTimer m_loginPollTimer; QTimer m_loginPollTimer;

View file

@ -10,12 +10,8 @@
#include <QFile> #include <QFile>
#include <QTimer> #include <QTimer>
#include <QDebug> #include <QDebug>
#include "nextcloudapi.h"
const QString STATUS_ENDPOINT("/status.php");
const QString LOGIN_ENDPOINT("/index.php/login/v2");
const QString USERS_ENDPOINT("/ocs/v1.php/cloud/users");
const QString CAPABILITIES_ENDPOINT("/ocs/v1.php/cloud/capabilities");
const QString APPPASSWORD_ENDPOINT("/ocs/v2.php/core/");
const QString NOTES_ENDPOINT("/index.php/apps/notes/api/v0.2/notes"); const QString NOTES_ENDPOINT("/index.php/apps/notes/api/v0.2/notes");
const QString EXCLUDE_QUERY("exclude="); const QString EXCLUDE_QUERY("exclude=");
const QString PURGE_QUERY("purgeBefore="); const QString PURGE_QUERY("purgeBefore=");

View file

@ -222,6 +222,33 @@
<translation>MIT Lizenz</translation> <translation>MIT Lizenz</translation>
</message> </message>
</context> </context>
<context>
<name>NextcloudApi</name>
<message>
<source>No error</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>No network connection available</source>
<translation type="unfinished">Netzwerk Verbindung nicht verfügbar</translation>
</message>
<message>
<source>Failed to communicate with the Nextcloud server</source>
<translation type="unfinished">Fehler bei der Server-Kommunikation</translation>
</message>
<message>
<source>An error occured while establishing an encrypted connection</source>
<translation type="unfinished">Fehler beim Aufbau einer verschlüsselten Kommunikation</translation>
</message>
<message>
<source>Could not authenticate to the Nextcloud instance</source>
<translation type="unfinished">Fehler bei der Authentifizierung am Server</translation>
</message>
<message>
<source>Unknown error</source>
<translation type="unfinished">Unbekannter Fehler</translation>
</message>
</context>
<context> <context>
<name>Note</name> <name>Note</name>
<message> <message>

View file

@ -222,6 +222,33 @@
<translation>MIT License</translation> <translation>MIT License</translation>
</message> </message>
</context> </context>
<context>
<name>NextcloudApi</name>
<message>
<source>No error</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>No network connection available</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Failed to communicate with the Nextcloud server</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>An error occured while establishing an encrypted connection</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Could not authenticate to the Nextcloud instance</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Unknown error</source>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>Note</name> <name>Note</name>
<message> <message>

View file

@ -272,6 +272,39 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
</context> </context>
<context>
<name>NextcloudApi</name>
<message>
<location filename="../src/nextcloudapi.cpp" line="157"/>
<source>No error</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/nextcloudapi.cpp" line="160"/>
<source>No network connection available</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/nextcloudapi.cpp" line="163"/>
<source>Failed to communicate with the Nextcloud server</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/nextcloudapi.cpp" line="166"/>
<source>An error occured while establishing an encrypted connection</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/nextcloudapi.cpp" line="169"/>
<source>Could not authenticate to the Nextcloud instance</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/nextcloudapi.cpp" line="172"/>
<source>Unknown error</source>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>Note</name> <name>Note</name>
<message> <message>
@ -879,27 +912,27 @@ You can also use other markdown syntax inside them.</source>
<context> <context>
<name>harbour-nextcloudnotes</name> <name>harbour-nextcloudnotes</name>
<message> <message>
<location filename="../qml/harbour-nextcloudnotes.qml" line="100"/> <location filename="../qml/harbour-nextcloudnotes.qml" line="101"/>
<source>Notes</source> <source>Notes</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/harbour-nextcloudnotes.qml" line="101"/> <location filename="../qml/harbour-nextcloudnotes.qml" line="102"/>
<source>Offline</source> <source>Offline</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/harbour-nextcloudnotes.qml" line="102"/> <location filename="../qml/harbour-nextcloudnotes.qml" line="103"/>
<source>Synced</source> <source>Synced</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/harbour-nextcloudnotes.qml" line="116"/> <location filename="../qml/harbour-nextcloudnotes.qml" line="117"/>
<source>API error</source> <source>API error</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/harbour-nextcloudnotes.qml" line="109"/> <location filename="../qml/harbour-nextcloudnotes.qml" line="110"/>
<source>File error</source> <source>File error</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>