diff --git a/qml/pages/LoginDialog.qml b/qml/pages/LoginDialog.qml index cec4172..9b16d60 100644 --- a/qml/pages/LoginDialog.qml +++ b/qml/pages/LoginDialog.qml @@ -36,6 +36,7 @@ Dialog { if (addingNew) appSettings.removeAccount(accountId) notesApi.host = account.value("server", "", String) } + onDone: notesApi.abortFlowV2Login() Connections { target: notesApi @@ -170,23 +171,20 @@ Dialog { id: loginButton anchors.horizontalCenter: parent.horizontalCenter property bool pushed: false - text: qsTr("Login") - enabled: !pushed - onClicked: { - pushed = true - loginTimeout.start() - notesApi.initiateFlowV2Login() - } - BusyIndicator { - id: loginBusyIndicator - anchors.centerIn: parent - running: loginButton.pushed - } - Timer { - id: loginTimeout - interval: 60000 - onTriggered: loginButton.pushed = false - } + text: notesApi.loginBusy ? qsTr("Abort") : qsTr("Login") + onClicked: notesApi.loginBusy ? notesApi.abortFlowV2Login() : notesApi.initiateFlowV2Login() + } + ProgressBar { + id: loginProgressBar + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width + highlighted: notesApi.loginBusy + indeterminate: notesApi.loginUrl.toString() !== "" + label: indeterminate ? qsTr("Follow the login procedure") : "" + //anchors.verticalCenter: loginButton.verticalCenter + //anchors.left: loginButton.right + //anchors.leftMargin: Theme.paddingMedium + //running: notesApi.loginBusy } SectionHeader { diff --git a/src/notesapi.cpp b/src/notesapi.cpp index 1a6eb3c..006268a 100644 --- a/src/notesapi.cpp +++ b/src/notesapi.cpp @@ -4,11 +4,12 @@ #include #include -NotesApi::NotesApi(QObject *parent) : QObject(parent) +NotesApi::NotesApi(const QString statusEndpoint, const QString loginEndpoint, const QString notesEndpoint, QObject *parent) + : QObject(parent), m_statusEndpoint(statusEndpoint), m_loginEndpoint(loginEndpoint), m_notesEndpoint(notesEndpoint) { - m_loginPollTimer.setInterval(1000); + // TODO verify connections (also in destructor) + m_loginPollTimer.setInterval(5000); connect(&m_loginPollTimer, SIGNAL(timeout()), this, SLOT(pollLoginUrl())); - m_online = m_manager.networkAccessible() == QNetworkAccessManager::Accessible; mp_model = new NotesModel(this); mp_modelProxy = new NotesProxyModel(this); mp_modelProxy->setSourceModel(mp_model); @@ -17,14 +18,19 @@ NotesApi::NotesApi(QObject *parent) : QObject(parent) mp_modelProxy->setFilterCaseSensitivity(Qt::CaseInsensitive); mp_modelProxy->setFilterRole(NotesModel::ContentRole); connect(this, SIGNAL(urlChanged(QUrl)), this, SLOT(verifyUrl(QUrl))); + connect(this, SIGNAL(statusBusyChanged(bool)), this, SIGNAL(busyChanged(bool))); + connect(this, SIGNAL(loginBusyChanged(bool)), this, SIGNAL(busyChanged(bool))); + connect(this, SIGNAL(notesBusyChanged(bool)), this, SIGNAL(busyChanged(bool))); connect(&m_manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)), this, SLOT(requireAuthentication(QNetworkReply*,QAuthenticator*))); connect(&m_manager, SIGNAL(networkAccessibleChanged(QNetworkAccessManager::NetworkAccessibility)), this, SLOT(onNetworkAccessibleChanged(QNetworkAccessManager::NetworkAccessibility))); connect(&m_manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*))); connect(&m_manager, SIGNAL(sslErrors(QNetworkReply*,QList)), this, SLOT(sslError(QNetworkReply*,QList))); m_request.setSslConfiguration(QSslConfiguration::defaultConfiguration()); m_request.setHeader(QNetworkRequest::UserAgentHeader, QGuiApplication::applicationDisplayName() + " " + QGuiApplication::applicationVersion() + " - " + QSysInfo::machineHostName()); + m_request.setHeader(QNetworkRequest::ContentTypeHeader, QString("application/x-www-form-urlencoded").toUtf8()); m_request.setRawHeader("OCS-APIREQUEST", "true"); - m_request.setHeader(QNetworkRequest::ContentTypeHeader, QString("application/json").toUtf8()); + m_authenticatedRequest = m_request; + m_authenticatedRequest.setHeader(QNetworkRequest::ContentTypeHeader, QString("application/json").toUtf8()); connect(mp_model, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector)), this, SLOT(saveToFile(QModelIndex,QModelIndex,QVector))); } @@ -43,34 +49,24 @@ NotesApi::~NotesApi() { } void NotesApi::setSslVerify(bool verify) { - if (verify != (m_request.sslConfiguration().peerVerifyMode() == QSslSocket::VerifyPeer)) { - m_request.sslConfiguration().setPeerVerifyMode(verify ? QSslSocket::VerifyPeer : QSslSocket::VerifyNone); + if (verify != (m_authenticatedRequest.sslConfiguration().peerVerifyMode() == QSslSocket::VerifyPeer)) { + m_authenticatedRequest.sslConfiguration().setPeerVerifyMode(verify ? QSslSocket::VerifyPeer : QSslSocket::VerifyNone); emit sslVerifyChanged(verify); } } void NotesApi::setUrl(QUrl url) { if (url != m_url) { - QUrl oldUrl = m_url; QString oldServer = server(); - m_url = url; + setScheme(url.scheme()); + setHost(url.host()); + setPort(url.port()); + setUsername(url.userName()); + setPassword(url.password()); + setPath(url.path()); emit urlChanged(m_url); if (server() != oldServer) emit serverChanged(server()); - if (m_url.scheme() != oldUrl.scheme()) - emit schemeChanged(m_url.scheme()); - if (m_url.host() != oldUrl.host()) - emit hostChanged(m_url.host()); - if (m_url.port() != oldUrl.port()) - emit portChanged(m_url.port()); - if (m_url.userName() != oldUrl.userName()) - emit usernameChanged(m_url.userName()); - if (m_url.password() != oldUrl.password()) - emit passwordChanged(m_url.password()); - if (m_url.path() != oldUrl.path()) - emit pathChanged(m_url.path()); - if (m_url.isValid()) - qDebug() << "API URL:" << m_url.toDisplayString(); } } @@ -95,139 +91,135 @@ void NotesApi::setServer(QString serverUrl) { } void NotesApi::setScheme(QString scheme) { - if (scheme == "http" || scheme == "https") { - QUrl url = m_url; - url.setScheme(scheme); - setUrl(url); + if (scheme != m_url.scheme() && (scheme == "http" || scheme == "https")) { + m_url.setScheme(scheme); + emit schemeChanged(m_url.scheme()); + emit urlChanged(m_url); } } void NotesApi::setHost(QString host) { - if (!host.isEmpty()) { - QUrl url = m_url; - url.setHost(host); - setUrl(url); + if (host != m_url.host()) { + m_url.setHost(host); + emit hostChanged(m_url.host()); + emit urlChanged(m_url); } } void NotesApi::setPort(int port) { - if (port >= 1 && port <= 65535) { - QUrl url = m_url; - url.setPort(port); - setUrl(url); + if (port != m_url.port() && port >= 1 && port <= 65535) { + m_url.setPort(port); + emit portChanged(m_url.port()); + emit urlChanged(m_url); } } -void NotesApi::setUsername(QString user) { - if (!user.isEmpty()) { - QUrl url = m_url; - url.setUserName(user); - QString concatenated = user + ":" + password(); +void NotesApi::setUsername(QString username) { + if (username != m_url.userName()) { + m_url.setUserName(username); + QString concatenated = username + ":" + password(); QByteArray data = concatenated.toLocal8Bit().toBase64(); QString headerData = "Basic " + data; - m_request.setRawHeader("Authorization", headerData.toLocal8Bit()); - setUrl(url); + m_authenticatedRequest.setRawHeader("Authorization", headerData.toLocal8Bit()); + emit usernameChanged(m_url.userName()); + emit urlChanged(m_url); } } void NotesApi::setPassword(QString password) { - if (!password.isEmpty()) { - QUrl url = m_url; - url.setPassword(password); + if (password != m_url.password()) { + m_url.setPassword(password); QString concatenated = username() + ":" + password; QByteArray data = concatenated.toLocal8Bit().toBase64(); QString headerData = "Basic " + data; - m_request.setRawHeader("Authorization", headerData.toLocal8Bit()); - setUrl(url); + m_authenticatedRequest.setRawHeader("Authorization", headerData.toLocal8Bit()); + emit passwordChanged(m_url.password()); + emit urlChanged(m_url); } } void NotesApi::setPath(QString path) { - if (!path.isEmpty()) { - QUrl url = m_url; - url.setPath(path); - setUrl(url); + if (path != m_url.path()) { + m_url.setPath(path); + emit pathChanged(m_url.path()); + emit urlChanged(m_url); } } void NotesApi::setDataFile(QString dataFile) { if (dataFile != m_jsonFile.fileName()) { m_jsonFile.close(); - if (!dataFile.isEmpty()) - m_jsonFile.setFileName(dataFile); + m_jsonFile.setFileName(dataFile); emit dataFileChanged(m_jsonFile.fileName()); - //qDebug() << m_jsonFile.fileName(); } } -bool NotesApi::busy() const { - bool busy = false; - QVector replies; - replies << m_replies << m_status_replies << m_login_replies << m_poll_replies; - for (int i = 0; i < replies.size(); ++i) { - busy |= replies[i]->isRunning(); - } - return busy; -} - void NotesApi::getStatus() { - QUrl url = server(); - QNetworkRequest request; - url.setPath(url.path() + "/status.php"); + QUrl url = apiEndpointUrl(m_statusEndpoint); if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) { qDebug() << "POST" << url.toDisplayString(); - request.setUrl(url); - m_status_replies << m_manager.post(request, QByteArray()); - emit busyChanged(busy()); + m_request.setUrl(url); + m_statusReplies << m_manager.post(m_request, QByteArray()); + emit statusBusyChanged(true); } } void NotesApi::initiateFlowV2Login() { - QUrl url = server(); - url.setPath(url.path() + "/index.php/login/v2"); + QUrl url = apiEndpointUrl(m_loginEndpoint); if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) { qDebug() << "POST" << url.toDisplayString(); m_request.setUrl(url); - m_login_replies << m_manager.post(m_request, QByteArray()); + m_loginReplies << m_manager.post(m_request, QByteArray()); m_loginPollTimer.start(); - emit busyChanged(busy()); + emit loginBusyChanged(true); + } +} + +void NotesApi::abortFlowV2Login() { + // TODO crashes! + m_loginPollTimer.stop(); + m_loginUrl.clear(); + emit loginUrlChanged(m_loginUrl); + m_pollUrl.clear(); + m_pollToken.clear(); + for (int i = 0; i < m_loginReplies.size(); ++i) { + m_loginReplies[i]->abort(); + } + for (int i = 0; i < m_pollReplies.size(); ++i) { + m_pollReplies[i]->abort(); } } void NotesApi::pollLoginUrl() { - //QNetworkRequest request; - //request.setHeader(QNetworkRequest::ContentTypeHeader, QString("application/x-www-form-urlencoded").toUtf8()); if (m_pollUrl.isValid() && !m_pollUrl.scheme().isEmpty() && !m_pollUrl.host().isEmpty() && !m_pollToken.isEmpty()) { - //qDebug() << "POST" << m_pollUrl.toDisplayString(); + qDebug() << "POST" << m_pollUrl.toDisplayString(); m_request.setUrl(m_pollUrl); - m_poll_replies << m_manager.post(m_request, QByteArray("token=").append(m_pollToken)); - emit busyChanged(busy()); + m_pollReplies << m_manager.post(m_request, QByteArray("token=").append(m_pollToken)); + emit loginBusyChanged(true); } } void NotesApi::getAllNotes(QStringList excludeFields) { - QUrl url = server(); - url.setPath(url.path() + "/index.php/apps/notes/api/v0.2" + "/notes"); + QUrl url = apiEndpointUrl(m_notesEndpoint + "/notes"); if (!excludeFields.isEmpty()) url.setQuery(QString("exclude=").append(excludeFields.join(","))); if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) { qDebug() << "GET" << url.toDisplayString(); - m_request.setUrl(url); - m_replies << m_manager.get(m_request); - emit busyChanged(busy()); + m_authenticatedRequest.setUrl(url); + m_notesReplies << m_manager.get(m_authenticatedRequest); + emit notesBusyChanged(true); } } void NotesApi::getNote(double noteId, QStringList excludeFields) { - QUrl url = m_url; - url.setPath(url.path() + "/index.php/apps/notes/api/v0.2" + QString("/notes/%1").arg(noteId)); + QUrl url = apiEndpointUrl(m_notesEndpoint + QString("/notes/%1").arg(noteId)); if (!excludeFields.isEmpty()) url.setQuery(QString("exclude=").append(excludeFields.join(","))); if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) { qDebug() << "GET" << url.toDisplayString(); - m_request.setUrl(url); - m_replies << m_manager.get(m_request); - emit busyChanged(busy()); + m_authenticatedRequest.setUrl(url); + m_notesReplies << m_manager.get(m_authenticatedRequest); + emit notesBusyChanged(true); } } @@ -237,13 +229,12 @@ void NotesApi::createNote(QVariantMap fields) { mp_model->insertNote(note); // Create note via the API - QUrl url = m_url; - url.setPath(url.path() + "/index.php/apps/notes/api/v0.2" + "/notes"); + QUrl url = apiEndpointUrl(m_notesEndpoint + "/notes"); if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) { qDebug() << "POST" << url.toDisplayString(); - m_request.setUrl(url); - m_replies << m_manager.post(m_request, note.toJsonDocument().toJson()); - emit busyChanged(busy()); + m_authenticatedRequest.setUrl(url); + m_notesReplies << m_manager.post(m_authenticatedRequest, note.toJsonDocument().toJson()); + emit notesBusyChanged(true); } } @@ -253,13 +244,12 @@ void NotesApi::updateNote(double noteId, QVariantMap fields) { mp_model->insertNote(note); // Update note on the server - QUrl url = m_url; - url.setPath(url.path() + "/index.php/apps/notes/api/v0.2" + QString("/notes/%1").arg(noteId)); + QUrl url = apiEndpointUrl(m_notesEndpoint + QString("/notes/%1").arg(noteId)); if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) { qDebug() << "PUT" << url.toDisplayString(); - m_request.setUrl(url); - m_replies << m_manager.put(m_request, note.toJsonDocument().toJson()); - emit busyChanged(busy()); + m_authenticatedRequest.setUrl(url); + m_notesReplies << m_manager.put(m_authenticatedRequest, note.toJsonDocument().toJson()); + emit notesBusyChanged(true); } } @@ -268,13 +258,12 @@ void NotesApi::deleteNote(double noteId) { mp_model->removeNote(noteId); // Remove note from the server - QUrl url = m_url; - url.setPath(url.path() + "/index.php/apps/notes/api/v0.2" + QString("/notes/%1").arg(noteId)); + QUrl url = apiEndpointUrl(m_notesEndpoint + QString("/notes/%1").arg(noteId)); if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) { qDebug() << "DELETE" << url.toDisplayString(); - m_request.setUrl(url); - m_replies << m_manager.deleteResource(m_request); - emit busyChanged(busy()); + m_authenticatedRequest.setUrl(url); + m_notesReplies << m_manager.deleteResource(m_authenticatedRequest); + emit notesBusyChanged(true); } mp_model->removeNote(noteId); } @@ -296,61 +285,80 @@ void NotesApi::requireAuthentication(QNetworkReply *reply, QAuthenticator *authe } void NotesApi::onNetworkAccessibleChanged(QNetworkAccessManager::NetworkAccessibility accessible) { - m_online = accessible == QNetworkAccessManager::Accessible; - emit networkAccessibleChanged(m_online); + emit networkAccessibleChanged(accessible == QNetworkAccessManager::Accessible); } void NotesApi::replyFinished(QNetworkReply *reply) { //qDebug() << reply->error() << reply->errorString(); - bool deleteReply = false; if (reply->error() == QNetworkReply::NoError) { emit error(NoError); + QByteArray data = reply->readAll(); QJsonDocument json = QJsonDocument::fromJson(data); - if (m_login_replies.contains(reply)) { + /*if (reply->url().toString().contains(m_loginEndpoint)) { + qDebug() << "Login reply"; + } + else if (reply->url() == m_pollUrl) { + qDebug() << "Poll reply"; + } + else if (reply->url().toString().contains(m_statusEndpoint)) { + qDebug() << "Status reply"; + } + else if (reply->url().toString().contains(m_notesEndpoint)) { + qDebug() << "Notes reply"; + }*/ + + if (m_loginReplies.contains(reply)) { + qDebug() << "Login reply"; if (json.isObject()) updateLoginFlow(json.object()); - m_login_replies.removeAll(reply); + m_loginReplies.removeAll(reply); + emit loginBusyChanged(loginBusy()); } - else if (m_poll_replies.contains(reply)) { + else if (m_pollReplies.contains(reply)) { + qDebug() << "Poll reply, finished"; if (json.isObject()) updateLoginCredentials(json.object()); - m_poll_replies.removeAll(reply); - m_loginPollTimer.stop(); - m_loginUrl.clear(); + m_pollReplies.removeAll(reply); + abortFlowV2Login(); + emit loginBusyChanged(loginBusy()); } - else if (m_status_replies.contains(reply)) { + else if (m_statusReplies.contains(reply)) { + qDebug() << "Status reply"; if (json.isObject()) updateStatus(json.object()); - m_status_replies.removeAll(reply); + m_statusReplies.removeAll(reply); + emit statusBusyChanged(statusBusy()); } - else { + else if (m_notesReplies.contains(reply)) { + qDebug() << "Notes reply"; if (mp_model) { if (mp_model->fromJsonDocument(json)) { m_lastSync = QDateTime::currentDateTimeUtc(); emit lastSyncChanged(m_lastSync); } } - m_replies.removeAll(reply); + m_notesReplies.removeAll(reply); + emit notesBusyChanged(notesBusy()); + } + else { + qDebug() << "Unknown reply"; } //qDebug() << data; - deleteReply = true; } else if (reply->error() == QNetworkReply::AuthenticationRequiredError) { emit error(AuthenticationError); - deleteReply = true; } else { - if (!m_poll_replies.contains(reply)) { - emit error(CommunicationError); - deleteReply = true; - } - else { + if (m_pollReplies.contains(reply)) { + qDebug() << "Poll reply"; //qDebug() << "Polling not finished yet" << m_pollUrl; } + else { + emit error(CommunicationError); + } } - emit busyChanged(busy()); - //if (deleteReply) reply->deleteLater(); + reply->deleteLater(); } void NotesApi::sslError(QNetworkReply *reply, const QList &errors) { @@ -373,6 +381,12 @@ void NotesApi::saveToFile(QModelIndex, QModelIndex, QVector) { emit error(LocalFileWriteError); } +QUrl NotesApi::apiEndpointUrl(const QString endpoint) const { + QUrl url = server(); + url.setPath(url.path() + endpoint); + return url; +} + void NotesApi::updateStatus(const QJsonObject &status) { if (!status.isEmpty()) { if (m_status_installed != status.value("installed").toBool()) { @@ -431,7 +445,6 @@ void NotesApi::updateLoginFlow(const QJsonObject &login) { url = login.value("login").toString(); if (m_loginUrl != url && url.isValid()) { m_loginUrl = url; - qDebug() << "Login URL: " << m_loginUrl; emit loginUrlChanged(m_loginUrl); } } @@ -443,13 +456,13 @@ void NotesApi::updateLoginCredentials(const QJsonObject &credentials) { QString appPassword; if (!credentials.isEmpty()) { serverAddr = credentials.value("server").toString(); - if (serverAddr != server()) + if (!serverAddr.isEmpty() && serverAddr != server()) setServer(serverAddr); loginName = credentials.value("loginName").toString(); - if (loginName != username()) + if (!loginName.isEmpty() && loginName != username()) setUsername(loginName); appPassword = credentials.value("appPassword").toString(); - if (appPassword != password()) + if (!appPassword.isEmpty() && appPassword != password()) setPassword(appPassword); } qDebug() << "Login successfull for user" << loginName << "on" << serverAddr; diff --git a/src/notesapi.h b/src/notesapi.h index 0ac8905..9ec7d01 100644 --- a/src/notesapi.h +++ b/src/notesapi.h @@ -10,15 +10,22 @@ #include #include "notesmodel.h" +#define STATUS_ENDPOINT "/status.php" +#define LOGIN_ENDPOINT "/index.php/login/v2" +#define NOTES_ENDPOINT "/index.php/apps/notes/api/v0.2" + class NotesApi : public QObject { Q_OBJECT public: - explicit NotesApi(QObject *parent = nullptr); + explicit NotesApi(const QString statusEndpoint = STATUS_ENDPOINT, + const QString loginEndpoint = LOGIN_ENDPOINT, + const QString notesEndpoint = NOTES_ENDPOINT, + QObject *parent = nullptr); virtual ~NotesApi(); Q_PROPERTY(bool sslVerify READ sslVerify WRITE setSslVerify NOTIFY sslVerifyChanged) - bool sslVerify() const { return m_request.sslConfiguration().peerVerifyMode() == QSslSocket::VerifyPeer; } + bool sslVerify() const { return m_authenticatedRequest.sslConfiguration().peerVerifyMode() == QSslSocket::VerifyPeer; } void setSslVerify(bool verify); Q_PROPERTY(QUrl url READ url WRITE setUrl NOTIFY urlChanged) @@ -61,13 +68,19 @@ public: void setDataFile(QString dataFile); Q_PROPERTY(bool networkAccessible READ networkAccessible NOTIFY networkAccessibleChanged) - bool networkAccessible() const { return m_online; } + bool networkAccessible() const { return m_manager.networkAccessible() == QNetworkAccessManager::Accessible; } Q_PROPERTY(QDateTime lastSync READ lastSync NOTIFY lastSyncChanged) QDateTime lastSync() const { return m_lastSync; } + Q_PROPERTY(bool statusBusy READ statusBusy NOTIFY statusBusyChanged) + bool statusBusy() const { return !m_statusReplies.empty(); } + Q_PROPERTY(bool loginBusy READ loginBusy NOTIFY loginBusyChanged) + bool loginBusy() const { return !m_loginReplies.empty() || !m_pollReplies.empty() || m_loginPollTimer.isActive(); } + Q_PROPERTY(bool notesBusy READ notesBusy NOTIFY notesBusyChanged) + bool notesBusy() const { return !m_notesReplies.empty(); } Q_PROPERTY(bool busy READ busy NOTIFY busyChanged) - bool busy() const; + bool busy() const { return statusBusy() | loginBusy() | notesBusy(); } Q_PROPERTY(bool statusInstalled READ statusInstalled NOTIFY statusInstalledChanged) bool statusInstalled() const { return m_status_installed; } @@ -90,6 +103,7 @@ public: Q_INVOKABLE void getStatus(); Q_INVOKABLE void initiateFlowV2Login(); + Q_INVOKABLE void abortFlowV2Login(); Q_INVOKABLE void getAllNotes(QStringList excludeFields = QStringList()); Q_INVOKABLE void getNote(double noteId, QStringList excludeFields = QStringList()); Q_INVOKABLE void createNote(QVariantMap fields = QVariantMap()); @@ -122,6 +136,9 @@ signals: void dataFileChanged(QString dataFile); void networkAccessibleChanged(bool accessible); void lastSyncChanged(QDateTime lastSync); + void statusBusyChanged(bool busy); + void loginBusyChanged(bool busy); + void notesBusyChanged(bool busy); void busyChanged(bool busy); void statusInstalledChanged(bool installed); void statusMaintenanceChanged(bool maintenance); @@ -146,18 +163,19 @@ private slots: void saveToFile(QModelIndex,QModelIndex,QVector); private: - bool m_online; - QDateTime m_lastSync; QUrl m_url; QNetworkAccessManager m_manager; QNetworkRequest m_request; - QVector m_replies; + QNetworkRequest m_authenticatedRequest; QFile m_jsonFile; NotesModel* mp_model; NotesProxyModel* mp_modelProxy; + QUrl apiEndpointUrl(const QString endpoint) const; + // Nextcloud status.php + const QString m_statusEndpoint; + QVector m_statusReplies; void updateStatus(const QJsonObject &status); - QVector m_status_replies; bool m_status_installed; bool m_status_maintenance; bool m_status_needsDbUpgrade; @@ -167,14 +185,21 @@ private: QString m_status_productname; bool m_status_extendedSupport; + // Nextcloud Login Flow v2 - https://docs.nextcloud.com/server/18/developer_manual/client_apis/LoginFlow/index.html#login-flow-v2 + const QString m_loginEndpoint; + QVector m_loginReplies; + QVector m_pollReplies; void updateLoginFlow(const QJsonObject &login); void updateLoginCredentials(const QJsonObject &credentials); - QVector m_login_replies; - QVector m_poll_replies; QTimer m_loginPollTimer; QUrl m_loginUrl; QUrl m_pollUrl; QString m_pollToken; + + // Nextcloud Notes API - https://github.com/nextcloud/notes/wiki/Notes-0.2 + const QString m_notesEndpoint; + QVector m_notesReplies; + QDateTime m_lastSync; }; #endif // NOTESAPI_H diff --git a/translations/harbour-nextcloudnotes-de.ts b/translations/harbour-nextcloudnotes-de.ts index 3d8b7a9..f757f73 100644 --- a/translations/harbour-nextcloudnotes-de.ts +++ b/translations/harbour-nextcloudnotes-de.ts @@ -159,6 +159,14 @@ Allow unencrypted connections + + Abort + + + + Follow the login procedure + + MITLicense diff --git a/translations/harbour-nextcloudnotes-sv.ts b/translations/harbour-nextcloudnotes-sv.ts index d303cb8..18dfb36 100644 --- a/translations/harbour-nextcloudnotes-sv.ts +++ b/translations/harbour-nextcloudnotes-sv.ts @@ -159,6 +159,14 @@ Allow unencrypted connections + + Abort + + + + Follow the login procedure + + MITLicense diff --git a/translations/harbour-nextcloudnotes.ts b/translations/harbour-nextcloudnotes.ts index 4b6c1b8..2153b66 100644 --- a/translations/harbour-nextcloudnotes.ts +++ b/translations/harbour-nextcloudnotes.ts @@ -130,68 +130,78 @@ - - + + Login - + Save - + Login Flow v2 - + Legacy Login - + Account name - + Nextcloud server - + Username - + Password - + + Abort + + + + + Follow the login procedure + + + + Security - + <strong>CAUTION: Your password will be saved without any encryption on the device!</strong><br>Please consider creating a dedicated app password! Open your Nextcloud in a browser and go to <i>Settings</i> → <i>Security</i>. - + Do not check certificates - + Enable this option to allow selfsigned certificates - + Allow unencrypted connections @@ -273,37 +283,37 @@ NotesApi - + No network connection available - + Failed to communicate with the Nextcloud server - + An error happened while reading from the local storage - + An error happened while writing to the local storage - + An error occured while establishing an encrypted connection - + Could not authenticate to the Nextcloud instance - + Unknown error