Further implementing NextcloudApi clann
This commit is contained in:
parent
538bc6c54f
commit
c68ec1ee57
9 changed files with 368 additions and 130 deletions
|
@ -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 \
|
||||||
|
|
|
@ -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: {
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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=");
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
Loading…
Reference in a new issue