Implemented status.php call in the API. Began implementing Nextcloud Login flow v2.

This commit is contained in:
Scharel Clemens 2020-01-14 14:47:42 +01:00
parent da29b4b0ff
commit 58ae28dbe6
16 changed files with 197 additions and 35 deletions

View file

@ -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 \

View file

@ -1,4 +1,4 @@
import QtQuick 2.0
import QtQuick 2.2
import Sailfish.Silica 1.0
CoverBackground {

View file

@ -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

View file

@ -1,4 +1,4 @@
import QtQuick 2.0
import QtQuick 2.2
import Sailfish.Silica 1.0
Page {

View file

@ -1,4 +1,4 @@
import QtQuick 2.0
import QtQuick 2.2
import Sailfish.Silica 1.0
import Nemo.Notifications 1.0

View file

@ -1,4 +1,4 @@
import QtQuick 2.0
import QtQuick 2.2
import Sailfish.Silica 1.0
Page {

View file

@ -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")

View file

@ -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
}
}
}
}

View file

@ -1,4 +1,4 @@
import QtQuick 2.0
import QtQuick 2.2
import Sailfish.Silica 1.0
Page {

View file

@ -1,4 +1,4 @@
import QtQuick 2.0
import QtQuick 2.2
import Sailfish.Silica 1.0
import harbour.nextcloudnotes.notesmodel 1.0

View file

@ -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

View file

@ -1,4 +1,4 @@
import QtQuick 2.0
import QtQuick 2.2
import Sailfish.Silica 1.0
Page {

View file

@ -1,4 +1,4 @@
import QtQuick 2.0
import QtQuick 2.2
import Sailfish.Silica 1.0
Dialog {

View file

@ -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<int>) {
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<int> 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) {

View file

@ -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<int> statusVersion READ statusVersion NOTIFY statusVersionChanged)
QVector<int> 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<int> 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<QNetworkReply*> m_status_replies;
bool m_status_installed;
bool m_status_maintenance;
bool m_status_needsDbUpgrade;
QVector<int> m_status_version;
QString m_status_versionstring;
QString m_status_edition;
QString m_status_productname;
bool m_status_extendedSupport;
};
#endif // NOTESAPI_H

View file

@ -125,57 +125,58 @@
<context>
<name>LoginDialog</name>
<message>
<location filename="../qml/pages/LoginDialog.qml" line="50"/>
<location filename="../qml/pages/LoginDialog.qml" line="51"/>
<location filename="../qml/pages/LoginDialog.qml" line="120"/>
<source>Login</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/LoginDialog.qml" line="50"/>
<location filename="../qml/pages/LoginDialog.qml" line="51"/>
<source>Save</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/LoginDialog.qml" line="64"/>
<location filename="../qml/pages/LoginDialog.qml" line="67"/>
<source>Account name</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/LoginDialog.qml" line="79"/>
<location filename="../qml/pages/LoginDialog.qml" line="82"/>
<source>Nextcloud server</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/LoginDialog.qml" line="93"/>
<location filename="../qml/pages/LoginDialog.qml" line="97"/>
<source>Username</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/LoginDialog.qml" line="106"/>
<location filename="../qml/pages/LoginDialog.qml" line="110"/>
<source>Password</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/LoginDialog.qml" line="115"/>
<location filename="../qml/pages/LoginDialog.qml" line="125"/>
<source>Security</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/LoginDialog.qml" line="122"/>
<location filename="../qml/pages/LoginDialog.qml" line="132"/>
<source>&lt;strong&gt;CAUTION: Your password will be saved without any encryption on the device!&lt;/strong&gt;&lt;br&gt;Please consider creating a dedicated app password! Open your Nextcloud in a browser and go to &lt;i&gt;Settings&lt;/i&gt; &lt;i&gt;Security&lt;/i&gt;.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/LoginDialog.qml" line="126"/>
<location filename="../qml/pages/LoginDialog.qml" line="136"/>
<source>Do not check certificates</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/LoginDialog.qml" line="127"/>
<location filename="../qml/pages/LoginDialog.qml" line="137"/>
<source>Enable this option to allow selfsigned certificates</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/LoginDialog.qml" line="133"/>
<location filename="../qml/pages/LoginDialog.qml" line="143"/>
<source>Allow unencrypted connections</source>
<translation type="unfinished"></translation>
</message>
@ -262,37 +263,37 @@
<context>
<name>NotesApi</name>
<message>
<location filename="../src/notesapi.cpp" line="299"/>
<location filename="../src/notesapi.cpp" line="362"/>
<source>No network connection available</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/notesapi.cpp" line="302"/>
<location filename="../src/notesapi.cpp" line="365"/>
<source>Failed to communicate with the Nextcloud server</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/notesapi.cpp" line="305"/>
<location filename="../src/notesapi.cpp" line="368"/>
<source>An error happened while reading from the local storage</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/notesapi.cpp" line="308"/>
<location filename="../src/notesapi.cpp" line="371"/>
<source>An error happened while writing to the local storage</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/notesapi.cpp" line="311"/>
<location filename="../src/notesapi.cpp" line="374"/>
<source>An error occured while establishing an encrypted connection</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/notesapi.cpp" line="314"/>
<location filename="../src/notesapi.cpp" line="377"/>
<source>Could not authenticate to the Nextcloud instance</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/notesapi.cpp" line="317"/>
<location filename="../src/notesapi.cpp" line="380"/>
<source>Unknown error</source>
<translation type="unfinished"></translation>
</message>