diff --git a/harbour-nextcloudnotes.pro b/harbour-nextcloudnotes.pro index 1df938e..9dd260d 100644 --- a/harbour-nextcloudnotes.pro +++ b/harbour-nextcloudnotes.pro @@ -28,6 +28,7 @@ SOURCES += src/harbour-nextcloudnotes.cpp \ DISTFILES += qml/harbour-nextcloudnotes.qml \ qml/cover/CoverPage.qml \ + qml/pages/LoginWebView.qml \ rpm/harbour-nextcloudnotes.changes.run.in \ rpm/harbour-nextcloudnotes.changes \ rpm/harbour-nextcloudnotes.spec \ diff --git a/qml/cover/CoverPage.qml b/qml/cover/CoverPage.qml index a86be1c..0a4ed9f 100644 --- a/qml/cover/CoverPage.qml +++ b/qml/cover/CoverPage.qml @@ -1,4 +1,4 @@ -import QtQuick 2.0 +import QtQuick 2.2 import Sailfish.Silica 1.0 CoverBackground { diff --git a/qml/harbour-nextcloudnotes.qml b/qml/harbour-nextcloudnotes.qml index dc6176a..7faa565 100644 --- a/qml/harbour-nextcloudnotes.qml +++ b/qml/harbour-nextcloudnotes.qml @@ -1,4 +1,4 @@ -import QtQuick 2.0 +import QtQuick 2.2 import Sailfish.Silica 1.0 import Nemo.Configuration 1.0 import Nemo.Notifications 1.0 diff --git a/qml/pages/AboutPage.qml b/qml/pages/AboutPage.qml index dac414f..94f35c8 100644 --- a/qml/pages/AboutPage.qml +++ b/qml/pages/AboutPage.qml @@ -1,4 +1,4 @@ -import QtQuick 2.0 +import QtQuick 2.2 import Sailfish.Silica 1.0 Page { diff --git a/qml/pages/EditPage.qml b/qml/pages/EditPage.qml index a0e9fd1..08d4da2 100644 --- a/qml/pages/EditPage.qml +++ b/qml/pages/EditPage.qml @@ -1,4 +1,4 @@ -import QtQuick 2.0 +import QtQuick 2.2 import Sailfish.Silica 1.0 import Nemo.Notifications 1.0 diff --git a/qml/pages/GPLLicense.qml b/qml/pages/GPLLicense.qml index ff53929..4db6145 100644 --- a/qml/pages/GPLLicense.qml +++ b/qml/pages/GPLLicense.qml @@ -1,4 +1,4 @@ -import QtQuick 2.0 +import QtQuick 2.2 import Sailfish.Silica 1.0 Page { diff --git a/qml/pages/LoginDialog.qml b/qml/pages/LoginDialog.qml index c52e1fe..8bdada8 100644 --- a/qml/pages/LoginDialog.qml +++ b/qml/pages/LoginDialog.qml @@ -1,4 +1,4 @@ -import QtQuick 2.0 +import QtQuick 2.2 import Sailfish.Silica 1.0 import Nemo.Configuration 1.0 @@ -12,7 +12,7 @@ Dialog { id: account path: "/apps/harbour-nextcloudnotes/accounts/" + accountId Component.onCompleted: { - nameField.text = value("name", "", String) + //nameField.text = value("name", "", String) serverField.text = value("server", "", String) usernameField.text = value("username", "", String) passwordField.text = value("password", "", String) @@ -35,6 +35,7 @@ Dialog { } onRejected: { if (addingNew) appSettings.removeAccount(accountId) + notesApi.host = value("server", "", String) } SilicaFlickable { @@ -61,6 +62,8 @@ Dialog { id: nameField width: parent.width //text: account.value("name", "", String) + text: notesApi.statusProductName + enabled: false placeholderText: qsTr("Account name") label: placeholderText errorHighlight: text.length === 0// && focus === true @@ -84,6 +87,7 @@ Dialog { EnterKey.enabled: acceptableInput EnterKey.iconSource: "image://theme/icon-m-enter-next" EnterKey.onClicked: usernameField.focus = true + onTextChanged: notesApi.host = text } TextField { @@ -110,6 +114,12 @@ Dialog { EnterKey.iconSource: "image://theme/icon-m-enter-accept" EnterKey.onClicked: loginDialog.accept() } + Button { + id: loginButton + anchors.horizontalCenter: parent.horizontalCenter + text: qsTr("Login") + onClicked: pageStack.push(Qt.resolvedUrl("LoginWebView.qml"), { server: serverField.text }) + } SectionHeader { text: qsTr("Security") diff --git a/qml/pages/LoginWebView.qml b/qml/pages/LoginWebView.qml new file mode 100644 index 0000000..fb80298 --- /dev/null +++ b/qml/pages/LoginWebView.qml @@ -0,0 +1,50 @@ +import QtQuick 2.2 +import Sailfish.Silica 1.0 + +Page { + id: loginWebView + property string server + property url ncurl: (account.allowUnecrypted ? "http://" : "https://") + server + "/index.php/login/flow" + + Component.onCompleted: { + var req = new XMLHttpRequest() + req.open("GET", endpoint, true) + req.setRequestHeader('User-Agent', 'SailfishOS/harbour-nextcloudnotes') + req.setRequestHeader("OCS-APIREQUEST", "true") + req.onreadystatechange = function() { + if (req.readyState === XMLHttpRequest.DONE) { + if (req.status === 200) { + ncFlowWebView.data = req.responseText + } + } + } + req.send() + } + + SilicaWebView { + id: ncFlowWebView + anchors.fill: parent + + //url: ncurl + //experimental.userAgent: "SailfishBrowser 1 - Sailfish" //"Mozilla/5.0 (U; Linux; Maemo; Jolla; Sailfish; like Android 4.3) " + "AppleWebKit/" + wkversion + " (KHTML, like Gecko) WebPirate/" + version + " like Mobile Safari/" + wkversion + " (compatible)" + onNavigationRequested: { + console.log(url) + if (url.toString().indexOf("nc://login") === 0) { + var credentials = url.split("/", 1) + console.log(credentials) + } + } + + header: PageHeader { + title: ncFlowWebView.title + description: loginWebView.ncurl + BusyIndicator { + anchors.verticalCenter: parent.verticalCenter + anchors.right: parent.right + anchors.rightMargin: Theme.horizontalPageMargin + size: BusyIndicatorSize.Medium + running: ncFlowWebView.loading + } + } + } +} diff --git a/qml/pages/MITLicense.qml b/qml/pages/MITLicense.qml index 6a91ee6..476ff57 100644 --- a/qml/pages/MITLicense.qml +++ b/qml/pages/MITLicense.qml @@ -1,4 +1,4 @@ -import QtQuick 2.0 +import QtQuick 2.2 import Sailfish.Silica 1.0 Page { diff --git a/qml/pages/NotesPage.qml b/qml/pages/NotesPage.qml index de339c1..5d0d8da 100644 --- a/qml/pages/NotesPage.qml +++ b/qml/pages/NotesPage.qml @@ -1,4 +1,4 @@ -import QtQuick 2.0 +import QtQuick 2.2 import Sailfish.Silica 1.0 import harbour.nextcloudnotes.notesmodel 1.0 diff --git a/qml/pages/SettingsPage.qml b/qml/pages/SettingsPage.qml index 93465f4..7149736 100644 --- a/qml/pages/SettingsPage.qml +++ b/qml/pages/SettingsPage.qml @@ -1,4 +1,4 @@ -import QtQuick 2.0 +import QtQuick 2.2 import Sailfish.Silica 1.0 import Nemo.Configuration 1.0 import Nemo.Notifications 1.0 diff --git a/qml/pages/SyntaxPage.qml b/qml/pages/SyntaxPage.qml index dafff88..8b6aba2 100644 --- a/qml/pages/SyntaxPage.qml +++ b/qml/pages/SyntaxPage.qml @@ -1,4 +1,4 @@ -import QtQuick 2.0 +import QtQuick 2.2 import Sailfish.Silica 1.0 Page { diff --git a/qml/pages/UnencryptedDialog.qml b/qml/pages/UnencryptedDialog.qml index 17d81f9..e21a7cc 100644 --- a/qml/pages/UnencryptedDialog.qml +++ b/qml/pages/UnencryptedDialog.qml @@ -1,4 +1,4 @@ -import QtQuick 2.0 +import QtQuick 2.2 import Sailfish.Silica 1.0 Dialog { diff --git a/src/notesapi.cpp b/src/notesapi.cpp index ce26261..7dc462c 100644 --- a/src/notesapi.cpp +++ b/src/notesapi.cpp @@ -154,6 +154,17 @@ bool NotesApi::busy() const { return busy; } +void NotesApi::getStatus() { + QUrl url = m_url; + url.setPath("/status.php"); + if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) { + qDebug() << "POST" << url.toDisplayString(); + m_request.setUrl(url); + m_status_replies << m_manager.post(m_request, QByteArray()); + emit busyChanged(busy()); + } +} + void NotesApi::getAllNotes(QStringList excludeFields) { QUrl url = m_url; url.setPath(url.path() + "/notes"); @@ -230,6 +241,9 @@ void NotesApi::deleteNote(double noteId) { void NotesApi::verifyUrl(QUrl url) { emit urlValidChanged(url.isValid()); + if (url.isValid()) { + getStatus(); + } } void NotesApi::requireAuthentication(QNetworkReply *reply, QAuthenticator *authenticator) { @@ -243,7 +257,6 @@ void NotesApi::requireAuthentication(QNetworkReply *reply, QAuthenticator *authe void NotesApi::onNetworkAccessibleChanged(QNetworkAccessManager::NetworkAccessibility accessible) { m_online = accessible == QNetworkAccessManager::Accessible; - //qDebug() << m_online; emit networkAccessibleChanged(m_online); } @@ -253,10 +266,17 @@ void NotesApi::replyFinished(QNetworkReply *reply) { emit error(NoError); QByteArray data = reply->readAll(); QJsonDocument json = QJsonDocument::fromJson(data); - if (mp_model) { - if (mp_model->fromJsonDocument(json)) { - m_lastSync = QDateTime::currentDateTimeUtc(); - emit lastSyncChanged(m_lastSync); + if (m_status_replies.contains(reply)) { + if (json.isObject()) { + updateStatus(json.object()); + } + } + else { + if (mp_model) { + if (mp_model->fromJsonDocument(json)) { + m_lastSync = QDateTime::currentDateTimeUtc(); + emit lastSyncChanged(m_lastSync); + } } } //qDebug() << data; @@ -266,6 +286,7 @@ void NotesApi::replyFinished(QNetworkReply *reply) { else emit error(CommunicationError); m_replies.removeAll(reply); + m_status_replies.removeAll(reply); reply->deleteLater(); emit busyChanged(busy()); } @@ -290,6 +311,48 @@ void NotesApi::saveToFile(QModelIndex, QModelIndex, QVector) { emit error(LocalFileWriteError); } +void NotesApi::updateStatus(const QJsonObject &status) { + if (!status.isEmpty()) { + if (m_status_installed != status.value("installed").toBool()) { + m_status_installed = status.value("installed").toBool(); + emit statusInstalledChanged(m_status_installed); + } + if (m_status_maintenance != status.value("maintenance").toBool()) { + m_status_maintenance = status.value("maintenance").toBool(); + emit statusMaintenanceChanged(m_status_maintenance); + } + if (m_status_needsDbUpgrade != status.value("needsDbUpgrade").toBool()) { + m_status_needsDbUpgrade = status.value("needsDbUpgrade").toBool(); + emit statusNeedsDbUpgradeChanged(m_status_needsDbUpgrade); + } + QStringList versionStr = status.value("version").toString().split('.'); + QVector version; + for(int i = 0; i < versionStr.size(); ++i) + version << versionStr[i].toInt(); + if (m_status_version != version) { + m_status_version = version; + emit statusVersionChanged(m_status_version); + } + if (m_status_versionstring != status.value("versionstring").toString()) { + m_status_versionstring = status.value("versionstring").toString(); + emit statusVersionStringChanged(m_status_versionstring); + } + if (m_status_edition != status.value("edition").toString()) { + m_status_edition = status.value("edition").toString(); + emit statusEditionChanged(m_status_edition); + } + if (m_status_productname != status.value("productname").toString()) { + m_status_productname = status.value("productname").toString(); + qDebug() << m_status_productname; + emit statusProductNameChanged(m_status_productname); + } + if (m_status_extendedSupport != status.value("extendedSupport").toBool()) { + m_status_extendedSupport = status.value("extendedSupport").toBool(); + emit statusExtendedSupportChanged(m_status_extendedSupport); + } + } +} + const QString NotesApi::errorMessage(int error) const { QString message; switch (error) { diff --git a/src/notesapi.h b/src/notesapi.h index 9e144e5..39a8190 100644 --- a/src/notesapi.h +++ b/src/notesapi.h @@ -67,6 +67,24 @@ public: Q_PROPERTY(bool busy READ busy NOTIFY busyChanged) bool busy() const; + Q_PROPERTY(bool statusInstalled READ statusInstalled NOTIFY statusInstalledChanged) + bool statusInstalled() const { return m_status_installed; } + Q_PROPERTY(bool statusMaintenance READ statusMaintenance NOTIFY statusMaintenanceChanged) + bool statusMaintenance() const { return m_status_maintenance; } + Q_PROPERTY(bool statusNeedsDbUpgrade READ statusNeedsDbUpgrade NOTIFY statusNeedsDbUpgradeChanged) + bool statusNeedsDbUpgrade() const { return m_status_needsDbUpgrade; } + Q_PROPERTY(QVector statusVersion READ statusVersion NOTIFY statusVersionChanged) + QVector statusVersion() const { return m_status_version; } + Q_PROPERTY(QString statusVersionString READ statusVersionString NOTIFY statusVersionStringChanged) + QString statusVersionString() const { return m_status_versionstring; } + Q_PROPERTY(QString statusEdition READ statusEdition NOTIFY statusEditionChanged) + QString statusEdition() const { return m_status_edition; } + Q_PROPERTY(QString statusProductName READ statusProductName NOTIFY statusProductNameChanged) + QString statusProductName() const { return m_status_productname; } + Q_PROPERTY(bool statusExtendedSupport READ statusExtendedSupport NOTIFY statusExtendedSupportChanged) + bool statusExtendedSupport() const { return m_status_extendedSupport; } + + Q_INVOKABLE void getStatus(); Q_INVOKABLE void getAllNotes(QStringList excludeFields = QStringList()); Q_INVOKABLE void getNote(double noteId, QStringList excludeFields = QStringList()); Q_INVOKABLE void createNote(QVariantMap fields = QVariantMap()); @@ -100,6 +118,14 @@ signals: void lastSyncChanged(QDateTime lastSync); void readyChanged(bool ready); void busyChanged(bool busy); + void statusInstalledChanged(bool installed); + void statusMaintenanceChanged(bool maintenance); + void statusNeedsDbUpgradeChanged(bool needsDbUpgrade); + void statusVersionChanged(QVector version); + void statusVersionStringChanged(QString versionString); + void statusEditionChanged(QString edition); + void statusProductNameChanged(QString productName); + void statusExtendedSupportChanged(bool extendedSupport); void error(int error); public slots: @@ -122,6 +148,17 @@ private: QFile m_jsonFile; NotesModel* mp_model; NotesProxyModel* mp_modelProxy; + + void updateStatus(const QJsonObject &status); + QVector m_status_replies; + bool m_status_installed; + bool m_status_maintenance; + bool m_status_needsDbUpgrade; + QVector m_status_version; + QString m_status_versionstring; + QString m_status_edition; + QString m_status_productname; + bool m_status_extendedSupport; }; #endif // NOTESAPI_H diff --git a/translations/harbour-nextcloudnotes.ts b/translations/harbour-nextcloudnotes.ts index 9d9aa90..ebcdde5 100644 --- a/translations/harbour-nextcloudnotes.ts +++ b/translations/harbour-nextcloudnotes.ts @@ -125,57 +125,58 @@ LoginDialog - + + Login - + Save - + Account name - + Nextcloud server - + Username - + Password - + 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 @@ -262,37 +263,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