Some work on login workflow and configuration handling
This commit is contained in:
parent
6d0a6d67d9
commit
af5acbc4a1
14 changed files with 319 additions and 350 deletions
|
@ -17,6 +17,7 @@ CONFIG += sailfishapp
|
||||||
DEFINES += APP_VERSION=\\\"$$VERSION\\\"
|
DEFINES += APP_VERSION=\\\"$$VERSION\\\"
|
||||||
|
|
||||||
HEADERS += src/note.h \
|
HEADERS += src/note.h \
|
||||||
|
src/accounthash.h \
|
||||||
src/notesapi.h \
|
src/notesapi.h \
|
||||||
src/notesmodel.h
|
src/notesmodel.h
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ CoverBackground {
|
||||||
|
|
||||||
CoverActionList {
|
CoverActionList {
|
||||||
id: coverAction
|
id: coverAction
|
||||||
enabled: appSettings.currentAccount.length > 0
|
enabled: account != null
|
||||||
|
|
||||||
CoverAction {
|
CoverAction {
|
||||||
iconSource: "image://theme/icon-cover-new"
|
iconSource: "image://theme/icon-cover-new"
|
||||||
|
|
|
@ -8,43 +8,14 @@ ApplicationWindow
|
||||||
{
|
{
|
||||||
id: appWindow
|
id: appWindow
|
||||||
|
|
||||||
// All configured accounts
|
|
||||||
ConfigurationValue {
|
|
||||||
id: accounts
|
|
||||||
key: appSettings.path + "/accountIDs"
|
|
||||||
defaultValue: [ ]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Current account in use
|
|
||||||
ConfigurationGroup {
|
|
||||||
id: account
|
|
||||||
path: "/apps/harbour-nextcloudnotes/accounts/" + appSettings.currentAccount
|
|
||||||
|
|
||||||
property string name: value("name", "", String)
|
|
||||||
property url server: value("server", "", String)
|
|
||||||
property string version: value("version", "v0.2", String)
|
|
||||||
property string username: value("username", "", String)
|
|
||||||
property string password: account.value("password", "", String)
|
|
||||||
property bool doNotVerifySsl: account.value("doNotVerifySsl", false, Boolean)
|
|
||||||
property bool allowUnecrypted: account.value("allowUnecrypted", false, Boolean)
|
|
||||||
property date update: value("update", "", Date)
|
|
||||||
onServerChanged: notesApi.server = server
|
|
||||||
onUsernameChanged: {
|
|
||||||
console.log("Username: " + username)
|
|
||||||
notesApi.username = username
|
|
||||||
}
|
|
||||||
onPasswordChanged: notesApi.password = password
|
|
||||||
onDoNotVerifySslChanged: notesApi.verifySsl = !doNotVerifySsl
|
|
||||||
onNameChanged: console.log("Using account: " + name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// General settings of the app
|
// General settings of the app
|
||||||
ConfigurationGroup {
|
ConfigurationGroup {
|
||||||
id: appSettings
|
id: appSettings
|
||||||
path: "/apps/harbour-nextcloudnotes/settings"
|
path: "/apps/harbour-nextcloudnotes"
|
||||||
|
|
||||||
property bool initialized: false
|
property bool initialized: false
|
||||||
property string currentAccount: value("currentAccount", "", String)
|
property var accounts: value("accounts", [], Array)
|
||||||
|
property string currentAccountIndex: value("currentAccountIndex", -1, Number)
|
||||||
property int autoSyncInterval: value("autoSyncInterval", 0, Number)
|
property int autoSyncInterval: value("autoSyncInterval", 0, Number)
|
||||||
property int previewLineCount: value("previewLineCount", 4, Number)
|
property int previewLineCount: value("previewLineCount", 4, Number)
|
||||||
property bool favoritesOnTop: value("favoritesOnTop", true, Boolean)
|
property bool favoritesOnTop: value("favoritesOnTop", true, Boolean)
|
||||||
|
@ -53,9 +24,12 @@ ApplicationWindow
|
||||||
property bool useMonoFont: value("useMonoFont", false, Boolean)
|
property bool useMonoFont: value("useMonoFont", false, Boolean)
|
||||||
property bool useCapitalX: value("useCapitalX", false, Boolean)
|
property bool useCapitalX: value("useCapitalX", false, Boolean)
|
||||||
|
|
||||||
onCurrentAccountChanged: {
|
onCurrentAccountIndexChanged: {
|
||||||
account.path = "/apps/harbour-nextcloudnotes/accounts/" + currentAccount
|
console.log("Current account index: " + currentAccountIndex)
|
||||||
notesModel.account = currentAccount
|
if (currentAccountIndex >= 0 && currentAccountIndex < accounts.length) {
|
||||||
|
account = accounts[currentAccountIndex]
|
||||||
|
console.log("Current account: " + account.username + "@" + account.url)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onSortByChanged: {
|
onSortByChanged: {
|
||||||
|
@ -68,53 +42,18 @@ ApplicationWindow
|
||||||
notesProxyModel.favoritesOnTop = favoritesOnTop
|
notesProxyModel.favoritesOnTop = favoritesOnTop
|
||||||
}
|
}
|
||||||
|
|
||||||
function addAccount() {
|
function createAccount(user, url) {
|
||||||
var uuid = uuidv4()
|
var hash = accountHash.hash(user, url)
|
||||||
var tmpIDs = accounts.value
|
console.log("Hash(" + user + "@" + url + ") = " + hash)
|
||||||
tmpIDs.push(uuid)
|
return hash
|
||||||
accounts.value = tmpIDs
|
|
||||||
accounts.sync()
|
|
||||||
return uuid
|
|
||||||
}
|
|
||||||
ConfigurationGroup {
|
|
||||||
id: removeHelperConfGroup
|
|
||||||
}
|
|
||||||
function removeAccount(uuid) {
|
|
||||||
autoSyncTimer.stop()
|
|
||||||
var tmpIDs = accounts.value
|
|
||||||
removeHelperConfGroup.path = "/apps/harbour-nextcloudnotes/accounts/" + uuid
|
|
||||||
for (var i = tmpIDs.length-1; i >= 0; i--) {
|
|
||||||
console.log(tmpIDs)
|
|
||||||
console.log("Checking:" + tmpIDs[i])
|
|
||||||
if (tmpIDs[i] === uuid) {
|
|
||||||
console.log("Found! Removing ...")
|
|
||||||
tmpIDs.splice(i, 1)
|
|
||||||
}
|
|
||||||
console.log(tmpIDs)
|
|
||||||
}
|
|
||||||
if (appSettings.currentAccount === uuid) {
|
|
||||||
appSettings.currentAccount = ""
|
|
||||||
for (var i = tmpIDs.length-1; i >= 0 && appSettings.currentAccount === ""; i--) {
|
|
||||||
if (tmpIDs[i] !== uuid) {
|
|
||||||
appSettings.currentAccount = tmpIDs[i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
removeHelperConfGroup.clear()
|
|
||||||
if (autoSyncInterval > 0 && appWindow.visible) {
|
|
||||||
autoSyncTimer.start()
|
|
||||||
}
|
|
||||||
accounts.value = tmpIDs
|
|
||||||
accounts.sync()
|
|
||||||
}
|
|
||||||
function uuidv4() {
|
|
||||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
|
||||||
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
|
|
||||||
return v.toString(16);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
function removeAccount(hash) {
|
||||||
|
accounts[hash] = null
|
||||||
|
currentAccount = -1 }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
property var account
|
||||||
|
|
||||||
Notification {
|
Notification {
|
||||||
id: offlineNotification
|
id: offlineNotification
|
||||||
expireTimeout: 0
|
expireTimeout: 0
|
||||||
|
|
|
@ -1,65 +1,41 @@
|
||||||
import QtQuick 2.2
|
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 NextcloudNotes 1.0
|
||||||
|
|
||||||
Dialog {
|
Dialog {
|
||||||
id: loginDialog
|
id: loginDialog
|
||||||
|
|
||||||
property string accountId
|
canAccept: false
|
||||||
|
|
||||||
property bool legacyLoginPossible: false
|
property bool legacyLoginPossible: false
|
||||||
property bool flowLoginV2Possible: false
|
property bool flowLoginV2Possible: false
|
||||||
|
|
||||||
|
property url server
|
||||||
|
property string username
|
||||||
|
property string password
|
||||||
|
property bool doNotVerifySsl: false
|
||||||
|
property bool allowUnecrypted: false
|
||||||
|
|
||||||
|
property string productName
|
||||||
|
property string version
|
||||||
|
|
||||||
onRejected: {
|
onRejected: {
|
||||||
appSettings.removeAccount(accountId)
|
|
||||||
}
|
}
|
||||||
onAccepted: {
|
onAccepted: {
|
||||||
|
appSettings.createAccount(username, server)
|
||||||
}
|
}
|
||||||
|
|
||||||
ConfigurationGroup {
|
Timer {
|
||||||
id: account
|
id: verifyServerTimer
|
||||||
path: "/apps/harbour-nextcloudnotes/accounts/" + accountId
|
onTriggered: notesApi.getNcStatus()
|
||||||
|
|
||||||
property string name: value("name", qsTr("Nextcloud Login"), String)
|
|
||||||
property url server: value("server", "", String)
|
|
||||||
property string version: value("version", "v0.2", String)
|
|
||||||
property string username: value("username", "", String)
|
|
||||||
property string password: account.value("password", "", String)
|
|
||||||
property bool doNotVerifySsl: account.value("doNotVerifySsl", false, Boolean)
|
|
||||||
property bool allowUnecrypted: account.value("allowUnecrypted", false, Boolean)
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
|
||||||
dialogHeader.title = name
|
|
||||||
serverField.text = server ? server : allowUnecrypted ? "http://" : "https://"
|
|
||||||
usernameField.text = username
|
|
||||||
passwordField.text = password
|
|
||||||
unsecureConnectionTextSwitch.checked = doNotVerifySsl
|
|
||||||
unencryptedConnectionTextSwitch.checked = allowUnecrypted
|
|
||||||
if (username !== "" && password !== "") {
|
|
||||||
notesApi.server = server
|
|
||||||
notesApi.username = username
|
|
||||||
notesApi.password = password
|
|
||||||
notesApi.verifySsl = !doNotVerifySsl
|
|
||||||
notesApi.verifyLogin()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*onStatusChanged: {
|
|
||||||
if (status === PageStatus.Activating)
|
|
||||||
notesApi.getNcStatus()
|
|
||||||
if (status === PageStatus.Deactivating)
|
|
||||||
notesApi.abortFlowV2Login()
|
|
||||||
}*/
|
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: notesApi
|
target: notesApi
|
||||||
onStatusInstalledChanged: {
|
onStatusInstalledChanged: {
|
||||||
if (notesApi.statusInstalled)
|
if (notesApi.statusInstalled)
|
||||||
serverField.focus = false
|
serverField.focus = false
|
||||||
else {
|
|
||||||
dialogHeader.title
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
onStatusVersionChanged: {
|
onStatusVersionChanged: {
|
||||||
if (notesApi.statusVersion) {
|
if (notesApi.statusVersion) {
|
||||||
|
@ -80,58 +56,77 @@ Dialog {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onStatusVersionStringChanged: {
|
onStatusVersionStringChanged: {
|
||||||
if (notesApi.statusVersionString)
|
if (notesApi.statusVersionString) {
|
||||||
dialogHeader.description = "Nextcloud " + notesApi.statusVersionString
|
version = notesApi.statusVersionString
|
||||||
|
console.log(notesApi.statusVersionString)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
onStatusProductNameChanged: {
|
onStatusProductNameChanged: {
|
||||||
if (notesApi.statusProductName) {
|
if (notesApi.statusProductName) {
|
||||||
dialogHeader.title = notesApi.statusProductName
|
productName = notesApi.statusProductName
|
||||||
account.name = notesApi.statusProductName
|
console.log(notesApi.statusProductName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onLoginStatusChanged: {
|
onLoginStatusChanged: {
|
||||||
|
loginDialog.canAccept = false
|
||||||
|
apiProgressBar.indeterminate = false
|
||||||
switch(notesApi.loginStatus) {
|
switch(notesApi.loginStatus) {
|
||||||
case notesApi.LoginLegacyReady:
|
case NotesApi.LoginLegacyReady:
|
||||||
|
console.log("LoginLegacyReady")
|
||||||
apiProgressBar.label = qsTr("Enter your credentials")
|
apiProgressBar.label = qsTr("Enter your credentials")
|
||||||
break;
|
break;
|
||||||
//case notesApi.LoginFlowV2Initiating:
|
case NotesApi.LoginFlowV2Initiating:
|
||||||
// break;
|
console.log("LoginFlowV2Initiating")
|
||||||
case notesApi.LoginFlowV2Polling:
|
apiProgressBar.indeterminate = true
|
||||||
apiProgressBar.label = qsTr("Follow the instructions in the browser")
|
|
||||||
break;
|
break;
|
||||||
case notesApi.LoginFlowV2Success:
|
case NotesApi.LoginFlowV2Polling:
|
||||||
|
console.log("LoginFlowV2Polling")
|
||||||
|
apiProgressBar.label = qsTr("Follow the instructions in the browser")
|
||||||
|
apiProgressBar.indeterminate = true
|
||||||
|
break;
|
||||||
|
case NotesApi.LoginFlowV2Success:
|
||||||
|
console.log("LoginFlowV2Success")
|
||||||
notesApi.verifyLogin()
|
notesApi.verifyLogin()
|
||||||
break;
|
break;
|
||||||
case notesApi.LoginFlowV2Failed:
|
case NotesApi.LoginFlowV2Failed:
|
||||||
|
console.log("LoginFlowV2Failed")
|
||||||
apiProgressBar.label = qsTr("Login failed!")
|
apiProgressBar.label = qsTr("Login failed!")
|
||||||
break
|
break
|
||||||
case notesApi.LoginSuccess:
|
case NotesApi.LoginSuccess:
|
||||||
|
console.log("LoginSuccess")
|
||||||
apiProgressBar.label = qsTr("Login successfull!")
|
apiProgressBar.label = qsTr("Login successfull!")
|
||||||
account.username = notesApi.username
|
loginDialog.canAccept = true
|
||||||
account.password = notesApi.password
|
|
||||||
appSettings.currentAccount = accountId
|
|
||||||
break;
|
break;
|
||||||
case notesApi.LoginFailed:
|
case NotesApi.LoginFailed:
|
||||||
|
console.log("LoginFailed")
|
||||||
apiProgressBar.label = qsTr("Login failed!")
|
apiProgressBar.label = qsTr("Login failed!")
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
console.log("None")
|
||||||
apiProgressBar.label = ""
|
apiProgressBar.label = ""
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onLoginUrlChanged: {
|
onLoginUrlChanged: {
|
||||||
if (notesApi.loginUrl) {
|
if (notesApi.loginUrl) {
|
||||||
Qt.openUrlExternally(notesApi.loginUrl)
|
Qt.openUrlExternally(notesApi.loginUrl)
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
console.log("Login successfull")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
onServerChanged: {
|
onServerChanged: {
|
||||||
if (notesApi.server) {
|
if (notesApi.server) {
|
||||||
console.log("Login server: " + notesApi.server)
|
console.log(notesApi.server)
|
||||||
account.server = notesApi.server
|
server = notesApi.server
|
||||||
serverField.text = notesApi.server
|
}
|
||||||
|
}
|
||||||
|
onUsernameChanged: {
|
||||||
|
if (notesApi.username) {
|
||||||
|
console.log(notesApi.username)
|
||||||
|
username = notesApi.username
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onPasswordChanged: {
|
||||||
|
if (notesApi.password) {
|
||||||
|
console.log("***")
|
||||||
|
password = notesApi.password
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -147,6 +142,7 @@ Dialog {
|
||||||
|
|
||||||
DialogHeader {
|
DialogHeader {
|
||||||
id: dialogHeader
|
id: dialogHeader
|
||||||
|
title: qsTr("Nextcloud Login")
|
||||||
}
|
}
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
|
@ -161,8 +157,6 @@ Dialog {
|
||||||
id: apiProgressBar
|
id: apiProgressBar
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
width: parent.width
|
width: parent.width
|
||||||
indeterminate: notesApi.loginStatus === notesApi.LoginFlowV2Initiating ||
|
|
||||||
notesApi.loginStatus === notesApi.LoginFlowV2Polling
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
|
@ -170,17 +164,18 @@ Dialog {
|
||||||
TextField {
|
TextField {
|
||||||
id: serverField
|
id: serverField
|
||||||
width: parent.width - statusIcon.width - Theme.horizontalPageMargin
|
width: parent.width - statusIcon.width - Theme.horizontalPageMargin
|
||||||
placeholderText: qsTr("Nextcloud server")
|
text: server
|
||||||
|
placeholderText: productName ? productName : qsTr("Nextcloud server")
|
||||||
label: placeholderText
|
label: placeholderText
|
||||||
validator: RegExpValidator { regExp: unencryptedConnectionTextSwitch.checked ? /^https?:\/\/([-a-zA-Z0-9@:%._\+~#=].*)/: /^https:\/\/([-a-zA-Z0-9@:%._\+~#=].*)/ }
|
validator: RegExpValidator { regExp: unencryptedConnectionTextSwitch.checked ? /^https?:\/\/([-a-zA-Z0-9@:%._\+~#=].*)/: /^https:\/\/([-a-zA-Z0-9@:%._\+~#=].*)/ }
|
||||||
inputMethodHints: Qt.ImhUrlCharactersOnly
|
inputMethodHints: Qt.ImhUrlCharactersOnly
|
||||||
onClicked: if (text === "") text = "https://"
|
onClicked: if (text === "") text = allowUnecrypted ? "http://" : "https://"
|
||||||
onTextChanged: {
|
onTextChanged: {
|
||||||
statusBusyIndicatorTimer.restart()
|
|
||||||
if (acceptableInput) {
|
if (acceptableInput) {
|
||||||
notesApi.server = text
|
notesApi.server = text
|
||||||
notesApi.getNcStatus()
|
|
||||||
}
|
}
|
||||||
|
verifyServerTimer.restart()
|
||||||
|
notesApi.getNcStatus()
|
||||||
}
|
}
|
||||||
//EnterKey.enabled: text.length > 0
|
//EnterKey.enabled: text.length > 0
|
||||||
EnterKey.iconSource: legacyLoginPossible ? "image://theme/icon-m-enter-next" : flowLoginV2Possible ? "image://theme/icon-m-enter-accept" : "image://theme/icon-m-enter-close"
|
EnterKey.iconSource: legacyLoginPossible ? "image://theme/icon-m-enter-next" : flowLoginV2Possible ? "image://theme/icon-m-enter-accept" : "image://theme/icon-m-enter-close"
|
||||||
|
@ -199,11 +194,7 @@ Dialog {
|
||||||
BusyIndicator {
|
BusyIndicator {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
size: BusyIndicatorSize.Medium
|
size: BusyIndicatorSize.Medium
|
||||||
running: notesApi.ncStatusStatus === notesApi.NextcloudBusy || (serverField.focus && statusBusyIndicatorTimer.running && !notesApi.statusInstalled)
|
running: notesApi.ncStatusStatus === notesApi.NextcloudBusy || (verifyServerTimer.running)
|
||||||
Timer {
|
|
||||||
id: statusBusyIndicatorTimer
|
|
||||||
interval: 200
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -212,10 +203,10 @@ Dialog {
|
||||||
id: forceLegacyButton
|
id: forceLegacyButton
|
||||||
visible: debug || !notesApi.statusInstalled
|
visible: debug || !notesApi.statusInstalled
|
||||||
text: qsTr("Enforce legacy login")
|
text: qsTr("Enforce legacy login")
|
||||||
|
automaticCheck: true
|
||||||
onCheckedChanged: {
|
onCheckedChanged: {
|
||||||
checked != checked
|
|
||||||
if (!checked) {
|
if (!checked) {
|
||||||
notesApi.getNcStatus()
|
verifyServerTimer.restart()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -243,6 +234,7 @@ Dialog {
|
||||||
TextField {
|
TextField {
|
||||||
id: usernameField
|
id: usernameField
|
||||||
width: parent.width
|
width: parent.width
|
||||||
|
text: username
|
||||||
placeholderText: qsTr("Username")
|
placeholderText: qsTr("Username")
|
||||||
label: placeholderText
|
label: placeholderText
|
||||||
inputMethodHints: Qt.ImhNoPredictiveText | Qt.ImhNoAutoUppercase
|
inputMethodHints: Qt.ImhNoPredictiveText | Qt.ImhNoAutoUppercase
|
||||||
|
@ -254,6 +246,7 @@ Dialog {
|
||||||
PasswordField {
|
PasswordField {
|
||||||
id: passwordField
|
id: passwordField
|
||||||
width: parent.width
|
width: parent.width
|
||||||
|
text: password
|
||||||
placeholderText: qsTr("Password")
|
placeholderText: qsTr("Password")
|
||||||
label: placeholderText
|
label: placeholderText
|
||||||
errorHighlight: text.length === 0// && focus === true
|
errorHighlight: text.length === 0// && focus === true
|
||||||
|
@ -292,29 +285,30 @@ Dialog {
|
||||||
}
|
}
|
||||||
TextSwitch {
|
TextSwitch {
|
||||||
id: unsecureConnectionTextSwitch
|
id: unsecureConnectionTextSwitch
|
||||||
|
checked: doNotVerifySsl
|
||||||
text: qsTr("Do not check certificates")
|
text: qsTr("Do not check certificates")
|
||||||
description: qsTr("Enable this option to allow selfsigned certificates")
|
description: qsTr("Enable this option to allow selfsigned certificates")
|
||||||
onCheckedChanged: {
|
onCheckedChanged: {
|
||||||
account.doNotVerifySsl = checked
|
notesApi.verifySsl = !checked
|
||||||
notesApi.verifySsl = !account.doNotVerifySsl
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TextSwitch {
|
TextSwitch {
|
||||||
id: unencryptedConnectionTextSwitch
|
id: unencryptedConnectionTextSwitch
|
||||||
|
checked: allowUnecrypted
|
||||||
automaticCheck: false
|
automaticCheck: false
|
||||||
text: qsTr("Allow unencrypted connections")
|
text: qsTr("Allow unencrypted connections")
|
||||||
description: qsTr("")
|
//description: qsTr("")
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (checked) {
|
if (checked) {
|
||||||
checked = false
|
allowUnecrypted = !checked
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
var dialog = pageStack.push(Qt.resolvedUrl("UnencryptedDialog.qml"))
|
var dialog = pageStack.push(Qt.resolvedUrl("UnencryptedDialog.qml"))
|
||||||
dialog.accepted.connect(function() {
|
dialog.accepted.connect(function() {
|
||||||
checked = true
|
allowUnecrypted = true
|
||||||
})
|
})
|
||||||
dialog.rejected.connect(function() {
|
dialog.rejected.connect(function() {
|
||||||
checked = false
|
allowUnecrypted = false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ Page {
|
||||||
|
|
||||||
onStatusChanged: {
|
onStatusChanged: {
|
||||||
if (status === PageStatus.Activating) {
|
if (status === PageStatus.Activating) {
|
||||||
if (accounts.value.length <= 0) {
|
if (appSettings.accounts.length <= 0) {
|
||||||
addAccountHint.restart()
|
addAccountHint.restart()
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -32,16 +32,16 @@ Page {
|
||||||
}
|
}
|
||||||
MenuItem {
|
MenuItem {
|
||||||
text: qsTr("Add note")
|
text: qsTr("Add note")
|
||||||
enabled: appSettings.currentAccount.length > 0 && notesApi.networkAccessible
|
enabled: account != null && notesApi.networkAccessible
|
||||||
onClicked: notesApi.createNote( { 'content': "", 'modified': new Date().valueOf() / 1000 } )
|
onClicked: notesApi.createNote( { 'content': "", 'modified': new Date().valueOf() / 1000 } )
|
||||||
}
|
}
|
||||||
MenuItem {
|
MenuItem {
|
||||||
text: notesApi.networkAccessible && !notesApi.busy ? qsTr("Reload") : qsTr("Updating...")
|
text: notesApi.networkAccessible && !notesApi.busy ? qsTr("Reload") : qsTr("Updating...")
|
||||||
enabled: appSettings.currentAccount.length > 0 && notesApi.networkAccessible && !notesApi.busy
|
enabled: account != null && notesApi.networkAccessible && !notesApi.busy
|
||||||
onClicked: notes.getAllNotes()
|
onClicked: notes.getAllNotes()
|
||||||
}
|
}
|
||||||
MenuLabel {
|
MenuLabel {
|
||||||
visible: appSettings.currentAccount.length > 0
|
visible: account != null
|
||||||
text: qsTr("Last update") + ": " + (
|
text: qsTr("Last update") + ": " + (
|
||||||
new Date(account.update).valueOf() !== 0 ?
|
new Date(account.update).valueOf() !== 0 ?
|
||||||
new Date(account.update).toLocaleString(Qt.locale(), Locale.ShortFormat) :
|
new Date(account.update).toLocaleString(Qt.locale(), Locale.ShortFormat) :
|
||||||
|
@ -224,7 +224,7 @@ Page {
|
||||||
|
|
||||||
ViewPlaceholder {
|
ViewPlaceholder {
|
||||||
id: noLoginPlaceholder
|
id: noLoginPlaceholder
|
||||||
enabled: accounts.value.length <= 0
|
enabled: appSettings.accounts.length <= 0
|
||||||
text: qsTr("No account yet")
|
text: qsTr("No account yet")
|
||||||
hintText: qsTr("Got to the settings to add an account")
|
hintText: qsTr("Got to the settings to add an account")
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,7 @@ Page {
|
||||||
}
|
}
|
||||||
Label {
|
Label {
|
||||||
id: noAccountsLabel
|
id: noAccountsLabel
|
||||||
visible: accounts.value.length <= 0
|
visible: appSettings.accounts.length <= 0
|
||||||
text: qsTr("No Nextcloud account yet")
|
text: qsTr("No Nextcloud account yet")
|
||||||
font.pixelSize: Theme.fontSizeLarge
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
color: Theme.secondaryHighlightColor
|
color: Theme.secondaryHighlightColor
|
||||||
|
@ -43,7 +43,7 @@ Page {
|
||||||
}
|
}
|
||||||
Repeater {
|
Repeater {
|
||||||
id: accountRepeater
|
id: accountRepeater
|
||||||
model: accounts.value
|
model: appSettings.accounts
|
||||||
|
|
||||||
delegate: ListItem {
|
delegate: ListItem {
|
||||||
id: accountListItem
|
id: accountListItem
|
||||||
|
@ -91,8 +91,7 @@ Page {
|
||||||
text: qsTr("Add account")
|
text: qsTr("Add account")
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
onClicked: {
|
onClicked: {
|
||||||
var newAccountID = appSettings.addAccount()
|
var login = pageStack.push(Qt.resolvedUrl("LoginPage.qml"), { accountId: "" })
|
||||||
var login = pageStack.push(Qt.resolvedUrl("LoginPage.qml"), { accountId: newAccountID, addingNew: true })
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ Dialog {
|
||||||
DialogHeader {
|
DialogHeader {
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
LinkedLabel {
|
||||||
x: Theme.horizontalPageMargin
|
x: Theme.horizontalPageMargin
|
||||||
width: parent.width - 2*x
|
width: parent.width - 2*x
|
||||||
wrapMode: Text.Wrap
|
wrapMode: Text.Wrap
|
||||||
|
|
14
src/accounthash.h
Normal file
14
src/accounthash.h
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
#ifndef ACCOUNTHASH_H
|
||||||
|
#define ACCOUNTHASH_H
|
||||||
|
#include <QObject>
|
||||||
|
#include <QCryptographicHash>
|
||||||
|
|
||||||
|
class AccountHash : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
Q_INVOKABLE QByteArray hash(const QString username, const QString url) {
|
||||||
|
return QCryptographicHash::hash(QString("%1@%2").arg(username).arg(url).toUtf8(), QCryptographicHash::Sha256);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // ACCOUNTHASH_H
|
|
@ -2,6 +2,7 @@
|
||||||
#include <sailfishapp.h>
|
#include <sailfishapp.h>
|
||||||
#include <QtQml>
|
#include <QtQml>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
#include "accounthash.h"
|
||||||
#include "note.h"
|
#include "note.h"
|
||||||
#include "notesapi.h"
|
#include "notesapi.h"
|
||||||
#include "notesmodel.h"
|
#include "notesmodel.h"
|
||||||
|
@ -17,6 +18,7 @@ int main(int argc, char *argv[])
|
||||||
|
|
||||||
qDebug() << app->applicationDisplayName() << app->applicationVersion();
|
qDebug() << app->applicationDisplayName() << app->applicationVersion();
|
||||||
|
|
||||||
|
AccountHash* accountHash = new AccountHash;
|
||||||
qRegisterMetaType<Note>();
|
qRegisterMetaType<Note>();
|
||||||
NotesModel* notesModel = new NotesModel;
|
NotesModel* notesModel = new NotesModel;
|
||||||
NotesProxyModel* notesProxyModel = new NotesProxyModel;
|
NotesProxyModel* notesProxyModel = new NotesProxyModel;
|
||||||
|
@ -29,12 +31,15 @@ int main(int argc, char *argv[])
|
||||||
NotesApi* notesApi = new NotesApi;
|
NotesApi* notesApi = new NotesApi;
|
||||||
notesModel->setNotesApi(notesApi);
|
notesModel->setNotesApi(notesApi);
|
||||||
|
|
||||||
|
qmlRegisterType<NotesApi>("NextcloudNotes", 1, 0, "NotesApi");
|
||||||
|
|
||||||
QQuickView* view = SailfishApp::createView();
|
QQuickView* view = SailfishApp::createView();
|
||||||
#ifdef QT_DEBUG
|
#ifdef QT_DEBUG
|
||||||
view->rootContext()->setContextProperty("debug", QVariant(true));
|
view->rootContext()->setContextProperty("debug", QVariant(true));
|
||||||
#else
|
#else
|
||||||
view->rootContext()->setContextProperty("debug", QVariant(false));
|
view->rootContext()->setContextProperty("debug", QVariant(false));
|
||||||
#endif
|
#endif
|
||||||
|
view->rootContext()->setContextProperty("accountHash", accountHash);
|
||||||
view->rootContext()->setContextProperty("notesModel", notesModel);
|
view->rootContext()->setContextProperty("notesModel", notesModel);
|
||||||
view->rootContext()->setContextProperty("notesProxyModel", notesProxyModel);
|
view->rootContext()->setContextProperty("notesProxyModel", notesProxyModel);
|
||||||
view->rootContext()->setContextProperty("notesApi", notesApi);
|
view->rootContext()->setContextProperty("notesApi", notesApi);
|
||||||
|
|
218
src/notesapi.cpp
218
src/notesapi.cpp
|
@ -32,7 +32,7 @@ NotesApi::NotesApi(const QString statusEndpoint, const QString loginEndpoint, co
|
||||||
m_request.setSslConfiguration(QSslConfiguration::defaultConfiguration());
|
m_request.setSslConfiguration(QSslConfiguration::defaultConfiguration());
|
||||||
m_request.setHeader(QNetworkRequest::UserAgentHeader, QGuiApplication::applicationDisplayName() + " " + QGuiApplication::applicationVersion() + " - " + QSysInfo::machineHostName());
|
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.setHeader(QNetworkRequest::ContentTypeHeader, QString("application/x-www-form-urlencoded").toUtf8());
|
||||||
m_request.setRawHeader("OCS-APIREQUEST", "true");
|
m_request.setRawHeader("OCS-APIRequest", "true");
|
||||||
m_request.setRawHeader("Accept", "application/json");
|
m_request.setRawHeader("Accept", "application/json");
|
||||||
m_authenticatedRequest = m_request;
|
m_authenticatedRequest = m_request;
|
||||||
m_authenticatedRequest.setHeader(QNetworkRequest::ContentTypeHeader, QString("application/json").toUtf8());
|
m_authenticatedRequest.setHeader(QNetworkRequest::ContentTypeHeader, QString("application/json").toUtf8());
|
||||||
|
@ -47,102 +47,7 @@ NotesApi::~NotesApi() {
|
||||||
disconnect(&m_manager, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)), this, SLOT(sslError(QNetworkReply*,QList<QSslError>)));
|
disconnect(&m_manager, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)), this, SLOT(sslError(QNetworkReply*,QList<QSslError>)));
|
||||||
}
|
}
|
||||||
|
|
||||||
const QList<int> NotesApi::noteIds() {
|
// Generic API properties
|
||||||
return m_syncedNotes.keys();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool NotesApi::noteExists(const int id) {
|
|
||||||
return m_syncedNotes.contains(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
int NotesApi::noteModified(const int id) {
|
|
||||||
return m_syncedNotes.value(id, -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool NotesApi::getAllNotes(const QStringList& exclude) {
|
|
||||||
qDebug() << "Getting all notes";
|
|
||||||
QUrl url = apiEndpointUrl(m_notesEndpoint);
|
|
||||||
|
|
||||||
if (!exclude.isEmpty())
|
|
||||||
url.setQuery(QString(EXCLUDE_QUERY).append(exclude.join(",")));
|
|
||||||
|
|
||||||
if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) {
|
|
||||||
qDebug() << "GET" << url.toDisplayString();
|
|
||||||
m_authenticatedRequest.setUrl(url);
|
|
||||||
m_getAllNotesReplies << m_manager.get(m_authenticatedRequest);
|
|
||||||
emit busyChanged(true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool NotesApi::getNote(const int id, const QStringList& exclude) {
|
|
||||||
qDebug() << "Getting note: " << id;
|
|
||||||
QUrl url = apiEndpointUrl(m_notesEndpoint + QString("/%1").arg(id));
|
|
||||||
if (!exclude.isEmpty())
|
|
||||||
url.setQuery(QString(EXCLUDE_QUERY).append(exclude.join(",")));
|
|
||||||
|
|
||||||
if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) {
|
|
||||||
qDebug() << "GET" << url.toDisplayString();
|
|
||||||
m_authenticatedRequest.setUrl(url);
|
|
||||||
m_getNoteReplies << m_manager.get(m_authenticatedRequest);
|
|
||||||
emit busyChanged(true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool NotesApi::createNote(const QJsonObject& note) {
|
|
||||||
qDebug() << "Creating note";
|
|
||||||
QUrl url = apiEndpointUrl(m_notesEndpoint);
|
|
||||||
if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) {
|
|
||||||
qDebug() << "POST" << url.toDisplayString();
|
|
||||||
m_authenticatedRequest.setUrl(url);
|
|
||||||
m_createNoteReplies << m_manager.post(m_authenticatedRequest, QJsonDocument(note).toJson());
|
|
||||||
emit busyChanged(true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool NotesApi::updateNote(const int id, const QJsonObject& note) {
|
|
||||||
qDebug() << "Updating note: " << id;
|
|
||||||
QUrl url = apiEndpointUrl(m_notesEndpoint + QString("/%1").arg(id));
|
|
||||||
if (id >= 0 && url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) {
|
|
||||||
qDebug() << "PUT" << url.toDisplayString();
|
|
||||||
m_authenticatedRequest.setUrl(url);
|
|
||||||
m_updateNoteReplies << m_manager.put(m_authenticatedRequest, QJsonDocument(note).toJson());
|
|
||||||
emit busyChanged(true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool NotesApi::deleteNote(const int id) {
|
|
||||||
qDebug() << "Deleting note: " << id;
|
|
||||||
QUrl url = apiEndpointUrl(m_notesEndpoint + QString("/%1").arg(id));
|
|
||||||
if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) {
|
|
||||||
qDebug() << "DELETE" << url.toDisplayString();
|
|
||||||
m_authenticatedRequest.setUrl(url);
|
|
||||||
m_deleteNoteReplies << m_manager.deleteResource(m_authenticatedRequest);
|
|
||||||
emit busyChanged(true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool NotesApi::busy() const {
|
|
||||||
return !(m_getAllNotesReplies.empty() &&
|
|
||||||
m_getNoteReplies.empty() &&
|
|
||||||
m_createNoteReplies.empty() &&
|
|
||||||
m_updateNoteReplies.empty() &&
|
|
||||||
m_deleteNoteReplies.empty() &&
|
|
||||||
m_statusReplies.empty() &&
|
|
||||||
m_loginReplies.empty() &&
|
|
||||||
m_pollReplies.empty() &&
|
|
||||||
m_ocsReplies.empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
void NotesApi::setVerifySsl(bool verify) {
|
void NotesApi::setVerifySsl(bool verify) {
|
||||||
if (verify != (m_request.sslConfiguration().peerVerifyMode() == QSslSocket::VerifyPeer)) {
|
if (verify != (m_request.sslConfiguration().peerVerifyMode() == QSslSocket::VerifyPeer)) {
|
||||||
m_request.sslConfiguration().setPeerVerifyMode(verify ? QSslSocket::VerifyPeer : QSslSocket::VerifyNone);
|
m_request.sslConfiguration().setPeerVerifyMode(verify ? QSslSocket::VerifyPeer : QSslSocket::VerifyNone);
|
||||||
|
@ -249,6 +154,20 @@ void NotesApi::setPath(QString path) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Class status information
|
||||||
|
bool NotesApi::busy() const {
|
||||||
|
return !(m_getAllNotesReplies.empty() &&
|
||||||
|
m_getNoteReplies.empty() &&
|
||||||
|
m_createNoteReplies.empty() &&
|
||||||
|
m_updateNoteReplies.empty() &&
|
||||||
|
m_deleteNoteReplies.empty() &&
|
||||||
|
m_statusReplies.empty() &&
|
||||||
|
m_loginReplies.empty() &&
|
||||||
|
m_pollReplies.empty() &&
|
||||||
|
m_ocsReplies.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Callable functions
|
||||||
bool NotesApi::getNcStatus() {
|
bool NotesApi::getNcStatus() {
|
||||||
QUrl url = apiEndpointUrl(m_statusEndpoint);
|
QUrl url = apiEndpointUrl(m_statusEndpoint);
|
||||||
qDebug() << "GET" << url.toDisplayString();
|
qDebug() << "GET" << url.toDisplayString();
|
||||||
|
@ -323,6 +242,90 @@ void NotesApi::verifyLogin(QString username, QString password) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const QList<int> NotesApi::noteIds() {
|
||||||
|
return m_syncedNotes.keys();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NotesApi::noteExists(const int id) {
|
||||||
|
return m_syncedNotes.contains(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
int NotesApi::noteModified(const int id) {
|
||||||
|
return m_syncedNotes.value(id, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NotesApi::getAllNotes(const QStringList& exclude) {
|
||||||
|
qDebug() << "Getting all notes";
|
||||||
|
QUrl url = apiEndpointUrl(m_notesEndpoint);
|
||||||
|
|
||||||
|
if (!exclude.isEmpty())
|
||||||
|
url.setQuery(QString(EXCLUDE_QUERY).append(exclude.join(",")));
|
||||||
|
|
||||||
|
if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) {
|
||||||
|
qDebug() << "GET" << url.toDisplayString();
|
||||||
|
m_authenticatedRequest.setUrl(url);
|
||||||
|
m_getAllNotesReplies << m_manager.get(m_authenticatedRequest);
|
||||||
|
emit busyChanged(true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NotesApi::getNote(const int id, const QStringList& exclude) {
|
||||||
|
qDebug() << "Getting note: " << id;
|
||||||
|
QUrl url = apiEndpointUrl(m_notesEndpoint + QString("/%1").arg(id));
|
||||||
|
if (!exclude.isEmpty())
|
||||||
|
url.setQuery(QString(EXCLUDE_QUERY).append(exclude.join(",")));
|
||||||
|
|
||||||
|
if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) {
|
||||||
|
qDebug() << "GET" << url.toDisplayString();
|
||||||
|
m_authenticatedRequest.setUrl(url);
|
||||||
|
m_getNoteReplies << m_manager.get(m_authenticatedRequest);
|
||||||
|
emit busyChanged(true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NotesApi::createNote(const QJsonObject& note) {
|
||||||
|
qDebug() << "Creating note";
|
||||||
|
QUrl url = apiEndpointUrl(m_notesEndpoint);
|
||||||
|
if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) {
|
||||||
|
qDebug() << "POST" << url.toDisplayString();
|
||||||
|
m_authenticatedRequest.setUrl(url);
|
||||||
|
m_createNoteReplies << m_manager.post(m_authenticatedRequest, QJsonDocument(note).toJson());
|
||||||
|
emit busyChanged(true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NotesApi::updateNote(const int id, const QJsonObject& note) {
|
||||||
|
qDebug() << "Updating note: " << id;
|
||||||
|
QUrl url = apiEndpointUrl(m_notesEndpoint + QString("/%1").arg(id));
|
||||||
|
if (id >= 0 && url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) {
|
||||||
|
qDebug() << "PUT" << url.toDisplayString();
|
||||||
|
m_authenticatedRequest.setUrl(url);
|
||||||
|
m_updateNoteReplies << m_manager.put(m_authenticatedRequest, QJsonDocument(note).toJson());
|
||||||
|
emit busyChanged(true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NotesApi::deleteNote(const int id) {
|
||||||
|
qDebug() << "Deleting note: " << id;
|
||||||
|
QUrl url = apiEndpointUrl(m_notesEndpoint + QString("/%1").arg(id));
|
||||||
|
if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) {
|
||||||
|
qDebug() << "DELETE" << url.toDisplayString();
|
||||||
|
m_authenticatedRequest.setUrl(url);
|
||||||
|
m_deleteNoteReplies << m_manager.deleteResource(m_authenticatedRequest);
|
||||||
|
emit busyChanged(true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
const QString NotesApi::errorMessage(int error) const {
|
const QString NotesApi::errorMessage(int error) const {
|
||||||
QString message;
|
QString message;
|
||||||
switch (error) {
|
switch (error) {
|
||||||
|
@ -385,7 +388,7 @@ void NotesApi::replyFinished(QNetworkReply *reply) {
|
||||||
|
|
||||||
QByteArray data = reply->readAll();
|
QByteArray data = reply->readAll();
|
||||||
//qDebug() << data;
|
//qDebug() << data;
|
||||||
qDebug() << reply->rawHeader("X-Notes-API-Versions");
|
//qDebug() << reply->rawHeader("X-Notes-API-Versions");
|
||||||
QJsonDocument json = QJsonDocument::fromJson(data);
|
QJsonDocument json = QJsonDocument::fromJson(data);
|
||||||
|
|
||||||
if (m_getAllNotesReplies.contains(reply)) {
|
if (m_getAllNotesReplies.contains(reply)) {
|
||||||
|
@ -435,25 +438,27 @@ void NotesApi::replyFinished(QNetworkReply *reply) {
|
||||||
updateLoginFlow(json.object());
|
updateLoginFlow(json.object());
|
||||||
else {
|
else {
|
||||||
m_loginStatus = LoginStatus::LoginFailed;
|
m_loginStatus = LoginStatus::LoginFailed;
|
||||||
emit loginStatusChanged(m_loginStatus);
|
setLoginStatus(m_loginStatus);
|
||||||
}
|
}
|
||||||
m_loginReplies.removeOne(reply);
|
m_loginReplies.removeOne(reply);
|
||||||
}
|
}
|
||||||
else if (m_pollReplies.contains(reply)) {
|
else if (m_pollReplies.contains(reply)) {
|
||||||
qDebug() << "Poll reply, finished";
|
qDebug() << "Poll reply";
|
||||||
if (reply->error() == QNetworkReply::NoError && json.isObject())
|
if (reply->error() == QNetworkReply::NoError && json.isObject()) {
|
||||||
updateLoginCredentials(json.object());
|
updateLoginCredentials(json.object());
|
||||||
|
m_pollReplies.removeOne(reply);
|
||||||
|
}
|
||||||
else if (reply->error() == QNetworkReply::ContentNotFoundError) {
|
else if (reply->error() == QNetworkReply::ContentNotFoundError) {
|
||||||
qDebug() << "Polling not finished yet" << reply->url().toDisplayString();
|
qDebug() << "Polling not finished yet" << reply->url().toDisplayString();
|
||||||
m_loginStatus = LoginStatus::LoginFlowV2Polling;
|
m_loginStatus = LoginStatus::LoginFlowV2Polling;
|
||||||
emit loginStatusChanged(m_loginStatus);
|
setLoginStatus(m_loginStatus);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
m_loginStatus = LoginStatus::LoginFailed;
|
m_loginStatus = LoginStatus::LoginFailed;
|
||||||
emit loginStatusChanged(m_loginStatus);
|
setLoginStatus(m_loginStatus);
|
||||||
|
m_pollReplies.removeOne(reply);
|
||||||
|
abortFlowV2Login();
|
||||||
}
|
}
|
||||||
m_pollReplies.removeOne(reply);
|
|
||||||
abortFlowV2Login();
|
|
||||||
}
|
}
|
||||||
else if (m_statusReplies.contains(reply)) {
|
else if (m_statusReplies.contains(reply)) {
|
||||||
qDebug() << "Status reply";
|
qDebug() << "Status reply";
|
||||||
|
@ -658,8 +663,9 @@ bool NotesApi::updateLoginCredentials(const QJsonObject &credentials) {
|
||||||
|
|
||||||
void NotesApi::setLoginStatus(LoginStatus status, bool *changed) {
|
void NotesApi::setLoginStatus(LoginStatus status, bool *changed) {
|
||||||
if (status != m_loginStatus) {
|
if (status != m_loginStatus) {
|
||||||
if (changed)
|
if (changed) {
|
||||||
*changed = true;
|
*changed = true;
|
||||||
|
}
|
||||||
m_loginStatus = status;
|
m_loginStatus = status;
|
||||||
emit loginStatusChanged(m_loginStatus);
|
emit loginStatusChanged(m_loginStatus);
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,11 +70,12 @@ public:
|
||||||
QObject *parent = nullptr);
|
QObject *parent = nullptr);
|
||||||
virtual ~NotesApi();
|
virtual ~NotesApi();
|
||||||
|
|
||||||
|
// Status codes
|
||||||
enum CapabilitiesStatus {
|
enum CapabilitiesStatus {
|
||||||
CapabilitiesUnknown, // Initial unknown state
|
CapabilitiesUnknown, // Initial unknown state
|
||||||
CapabilitiesBusy, // Gettin information
|
CapabilitiesBusy, // Gettin information
|
||||||
CapabilitiesSuccess, // Capabilities successfully read
|
CapabilitiesSuccess, // Capabilities successfully read
|
||||||
CapabilitiesStatusFailed // Faild to retreive capabilities
|
CapabilitiesFailed // Faild to retreive capabilities
|
||||||
};
|
};
|
||||||
Q_ENUM(CapabilitiesStatus)
|
Q_ENUM(CapabilitiesStatus)
|
||||||
|
|
||||||
|
@ -82,7 +83,7 @@ public:
|
||||||
NextcloudUnknown, // Initial unknown state
|
NextcloudUnknown, // Initial unknown state
|
||||||
NextcloudBusy, // Getting information from the nextcloud server
|
NextcloudBusy, // Getting information from the nextcloud server
|
||||||
NextcloudSuccess, // Got information about the nextcloud server
|
NextcloudSuccess, // Got information about the nextcloud server
|
||||||
NextcloudFailed // Error getting information from the nextcloud server, see error()
|
NextcloudFailed // Error getting information from the nextcloud server, see ErrorCodes
|
||||||
};
|
};
|
||||||
Q_ENUM(NextcloudStatus)
|
Q_ENUM(NextcloudStatus)
|
||||||
|
|
||||||
|
@ -94,18 +95,17 @@ public:
|
||||||
LoginFlowV2Success, // Finished login flow v2
|
LoginFlowV2Success, // Finished login flow v2
|
||||||
LoginFlowV2Failed, // An error in login flow v2
|
LoginFlowV2Failed, // An error in login flow v2
|
||||||
LoginSuccess, // Login has been verified successfull
|
LoginSuccess, // Login has been verified successfull
|
||||||
LoginFailed // Login has failed, see error()
|
LoginFailed // Login has failed, see ErrorCodes
|
||||||
};
|
};
|
||||||
Q_ENUM(LoginStatus)
|
Q_ENUM(LoginStatus)
|
||||||
|
|
||||||
|
// Generic API properties
|
||||||
bool verifySsl() const { return m_authenticatedRequest.sslConfiguration().peerVerifyMode() == QSslSocket::VerifyPeer; }
|
bool verifySsl() const { return m_authenticatedRequest.sslConfiguration().peerVerifyMode() == QSslSocket::VerifyPeer; }
|
||||||
void setVerifySsl(bool verify);
|
void setVerifySsl(bool verify);
|
||||||
|
|
||||||
QUrl url() const { return m_url; }
|
QUrl url() const { return m_url; }
|
||||||
void setUrl(QUrl url);
|
void setUrl(QUrl url);
|
||||||
|
|
||||||
bool urlValid() const { return m_url.isValid(); }
|
|
||||||
|
|
||||||
QString server() const;
|
QString server() const;
|
||||||
void setServer(QString server);
|
void setServer(QString server);
|
||||||
|
|
||||||
|
@ -127,17 +127,19 @@ public:
|
||||||
QString path() const { return m_url.path(); }
|
QString path() const { return m_url.path(); }
|
||||||
void setPath(QString path);
|
void setPath(QString path);
|
||||||
|
|
||||||
|
// Class status information
|
||||||
|
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; }
|
||||||
|
|
||||||
QDateTime lastSync() const { return m_lastSync; }
|
QDateTime lastSync() const { return m_lastSync; }
|
||||||
|
|
||||||
bool busy() const;
|
bool busy() const;
|
||||||
|
|
||||||
|
// Nextcloud capabilities
|
||||||
CapabilitiesStatus capabilitiesStatus() const { return m_capabilitiesStatus; }
|
CapabilitiesStatus capabilitiesStatus() const { return m_capabilitiesStatus; }
|
||||||
bool notesAppInstalled() const { return m_capabilities_notesInstalled; }
|
bool notesAppInstalled() const { return m_capabilities_notesInstalled; }
|
||||||
QStringList notesAppApiVersions() const { return m_capabilities_notesApiVersions; }
|
QStringList notesAppApiVersions() const { return m_capabilities_notesApiVersions; }
|
||||||
static QString notesAppApiUsedVersion() { return m_capabilities_implementedApiVersion.toString(); }
|
static QString notesAppApiUsedVersion() { return m_capabilities_implementedApiVersion.toString(); }
|
||||||
|
|
||||||
|
// Nextcloud status (status.php)
|
||||||
NextcloudStatus ncStatusStatus() const { return m_ncStatusStatus; }
|
NextcloudStatus ncStatusStatus() const { return m_ncStatusStatus; }
|
||||||
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; }
|
||||||
|
@ -148,9 +150,11 @@ public:
|
||||||
QString statusProductName() const { return m_status_productname; }
|
QString statusProductName() const { return m_status_productname; }
|
||||||
bool statusExtendedSupport() const { return m_status_extendedSupport; }
|
bool statusExtendedSupport() const { return m_status_extendedSupport; }
|
||||||
|
|
||||||
|
// Login status
|
||||||
LoginStatus loginStatus() const { return m_loginStatus; }
|
LoginStatus loginStatus() const { return m_loginStatus; }
|
||||||
QUrl loginUrl() const { return m_loginUrl; }
|
QUrl loginUrl() const { return m_loginUrl; }
|
||||||
|
|
||||||
|
// Callable functions
|
||||||
Q_INVOKABLE bool getNcStatus();
|
Q_INVOKABLE bool getNcStatus();
|
||||||
Q_INVOKABLE bool initiateFlowV2Login();
|
Q_INVOKABLE bool initiateFlowV2Login();
|
||||||
Q_INVOKABLE void abortFlowV2Login();
|
Q_INVOKABLE void abortFlowV2Login();
|
||||||
|
@ -174,6 +178,7 @@ public:
|
||||||
int noteModified(const int id);
|
int noteModified(const int id);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
|
// Notes API calls
|
||||||
Q_INVOKABLE bool getAllNotes(const QStringList& exclude = QStringList());
|
Q_INVOKABLE bool getAllNotes(const QStringList& exclude = QStringList());
|
||||||
Q_INVOKABLE bool getNote(const int id, const QStringList& exclude = QStringList());
|
Q_INVOKABLE bool getNote(const int id, const QStringList& exclude = QStringList());
|
||||||
Q_INVOKABLE bool createNote(const QJsonObject& note);
|
Q_INVOKABLE bool createNote(const QJsonObject& note);
|
||||||
|
@ -181,9 +186,9 @@ public slots:
|
||||||
Q_INVOKABLE bool deleteNote(const int id);
|
Q_INVOKABLE bool deleteNote(const int id);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
// Generic API properties
|
||||||
void verifySslChanged(bool verify);
|
void verifySslChanged(bool verify);
|
||||||
void urlChanged(QUrl url);
|
void urlChanged(QUrl url);
|
||||||
void urlValidChanged(bool valid);
|
|
||||||
void serverChanged(QString server);
|
void serverChanged(QString server);
|
||||||
void schemeChanged(QString scheme);
|
void schemeChanged(QString scheme);
|
||||||
void hostChanged(QString host);
|
void hostChanged(QString host);
|
||||||
|
@ -191,16 +196,20 @@ signals:
|
||||||
void usernameChanged(QString username);
|
void usernameChanged(QString username);
|
||||||
void passwordChanged(QString password);
|
void passwordChanged(QString password);
|
||||||
void pathChanged(QString path);
|
void pathChanged(QString path);
|
||||||
void dataFileChanged(QString dataFile);
|
|
||||||
|
// Class status information
|
||||||
|
void urlValidChanged(bool valid);
|
||||||
void networkAccessibleChanged(bool accessible);
|
void networkAccessibleChanged(bool accessible);
|
||||||
void lastSyncChanged(QDateTime lastSync);
|
void lastSyncChanged(QDateTime lastSync);
|
||||||
void busyChanged(bool busy);
|
void busyChanged(bool busy);
|
||||||
|
|
||||||
|
// Nextcloud capabilities
|
||||||
void capabilitiesStatusChanged(CapabilitiesStatus status);
|
void capabilitiesStatusChanged(CapabilitiesStatus status);
|
||||||
void notesAppInstalledChanged(bool installed);
|
void notesAppInstalledChanged(bool installed);
|
||||||
void notesAppApiVersionsChanged(QStringList versions);
|
void notesAppApiVersionsChanged(QStringList versions);
|
||||||
void notesAppApiUsedVersionChanged(QString version);
|
void notesAppApiUsedVersionChanged(QString version);
|
||||||
|
|
||||||
|
// Nextcloud status (status.php)
|
||||||
void ncStatusStatusChanged(NextcloudStatus status);
|
void ncStatusStatusChanged(NextcloudStatus status);
|
||||||
void statusInstalledChanged(bool installed);
|
void statusInstalledChanged(bool installed);
|
||||||
void statusMaintenanceChanged(bool maintenance);
|
void statusMaintenanceChanged(bool maintenance);
|
||||||
|
@ -211,9 +220,11 @@ signals:
|
||||||
void statusProductNameChanged(QString productName);
|
void statusProductNameChanged(QString productName);
|
||||||
void statusExtendedSupportChanged(bool extendedSupport);
|
void statusExtendedSupportChanged(bool extendedSupport);
|
||||||
|
|
||||||
|
// Login status
|
||||||
void loginStatusChanged(LoginStatus status);
|
void loginStatusChanged(LoginStatus status);
|
||||||
void loginUrlChanged(QUrl url);
|
void loginUrlChanged(QUrl url);
|
||||||
|
|
||||||
|
// Notes API updates
|
||||||
void noteCreated(int id, const QJsonObject& note);
|
void noteCreated(int id, const QJsonObject& note);
|
||||||
void noteUpdated(int id, const QJsonObject& note);
|
void noteUpdated(int id, const QJsonObject& note);
|
||||||
void noteDeleted(int id);
|
void noteDeleted(int id);
|
||||||
|
|
|
@ -104,7 +104,7 @@ void NotesModel::setNotesApi(NotesApi *notesApi) {
|
||||||
mp_notesApi = notesApi;
|
mp_notesApi = notesApi;
|
||||||
if (mp_notesApi) {
|
if (mp_notesApi) {
|
||||||
// connect stuff
|
// connect stuff
|
||||||
//connect(mp_notesApi, SIGNAL(accountChanged(QString)), this, SIGNAL(accountChanged(QString)));
|
connect(mp_notesApi, SIGNAL(accountChanged(QString)), this, SIGNAL(accountChanged(QString)));
|
||||||
connect(mp_notesApi, SIGNAL(noteCreated(int,QJsonObject)), this, SLOT(insert(int,QJsonObject)));
|
connect(mp_notesApi, SIGNAL(noteCreated(int,QJsonObject)), this, SLOT(insert(int,QJsonObject)));
|
||||||
connect(mp_notesApi, SIGNAL(noteUpdated(int,QJsonObject)), this, SLOT(update(int,QJsonObject)));
|
connect(mp_notesApi, SIGNAL(noteUpdated(int,QJsonObject)), this, SLOT(update(int,QJsonObject)));
|
||||||
connect(mp_notesApi, SIGNAL(noteDeleted(int)), this, SLOT(remove(int)));
|
connect(mp_notesApi, SIGNAL(noteDeleted(int)), this, SLOT(remove(int)));
|
||||||
|
|
|
@ -144,7 +144,7 @@
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Enable this option to allow selfsigned certificates</source>
|
<source>Enable this option to allow selfsigned certificates</source>
|
||||||
<translation>Auswählen im selbst signierte Zertifikate zu erlauben</translation>
|
<translation>Auswählen um selbst signierte Zertifikate zu erlauben</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Allow unencrypted connections</source>
|
<source>Allow unencrypted connections</source>
|
||||||
|
|
|
@ -138,103 +138,103 @@
|
||||||
<context>
|
<context>
|
||||||
<name>LoginPage</name>
|
<name>LoginPage</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/LoginPage.qml" line="23"/>
|
<location filename="../qml/pages/LoginPage.qml" line="147"/>
|
||||||
<source>Nextcloud Login</source>
|
<source>Nextcloud Login</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/LoginPage.qml" line="173"/>
|
<location filename="../qml/pages/LoginPage.qml" line="170"/>
|
||||||
<source>Nextcloud server</source>
|
<source>Nextcloud server</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/LoginPage.qml" line="246"/>
|
<location filename="../qml/pages/LoginPage.qml" line="240"/>
|
||||||
<source>Username</source>
|
<source>Username</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/LoginPage.qml" line="257"/>
|
<location filename="../qml/pages/LoginPage.qml" line="252"/>
|
||||||
<source>Password</source>
|
<source>Password</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/LoginPage.qml" line="232"/>
|
<location filename="../qml/pages/LoginPage.qml" line="225"/>
|
||||||
<source>Abort</source>
|
<source>Abort</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/LoginPage.qml" line="100"/>
|
<location filename="../qml/pages/LoginPage.qml" line="86"/>
|
||||||
<source>Follow the instructions in the browser</source>
|
<source>Follow the instructions in the browser</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/LoginPage.qml" line="109"/>
|
<location filename="../qml/pages/LoginPage.qml" line="99"/>
|
||||||
<source>Login successfull!</source>
|
<source>Login successfull!</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/LoginPage.qml" line="106"/>
|
<location filename="../qml/pages/LoginPage.qml" line="95"/>
|
||||||
<location filename="../qml/pages/LoginPage.qml" line="115"/>
|
<location filename="../qml/pages/LoginPage.qml" line="104"/>
|
||||||
<source>Login failed!</source>
|
<source>Login failed!</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/LoginPage.qml" line="95"/>
|
<location filename="../qml/pages/LoginPage.qml" line="78"/>
|
||||||
<source>Enter your credentials</source>
|
<source>Enter your credentials</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/LoginPage.qml" line="214"/>
|
<location filename="../qml/pages/LoginPage.qml" line="207"/>
|
||||||
<source>Enforce legacy login</source>
|
<source>Enforce legacy login</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/LoginPage.qml" line="232"/>
|
<location filename="../qml/pages/LoginPage.qml" line="225"/>
|
||||||
<source>Login</source>
|
<source>Login</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/LoginPage.qml" line="232"/>
|
<location filename="../qml/pages/LoginPage.qml" line="225"/>
|
||||||
<source>Re-Login</source>
|
<source>Re-Login</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/LoginPage.qml" line="266"/>
|
<location filename="../qml/pages/LoginPage.qml" line="261"/>
|
||||||
<source>Test Login</source>
|
<source>Test Login</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/LoginPage.qml" line="272"/>
|
<location filename="../qml/pages/LoginPage.qml" line="267"/>
|
||||||
<source>Note</source>
|
<source>Note</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/LoginPage.qml" line="280"/>
|
<location filename="../qml/pages/LoginPage.qml" line="275"/>
|
||||||
<source>The <a href="https://apps.nextcloud.com/apps/notes">Notes</a> app needs to be installed on the Nextcloud server for this app to work.</source>
|
<source>The <a href="https://apps.nextcloud.com/apps/notes">Notes</a> app needs to be installed on the Nextcloud server for this app to work.</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/LoginPage.qml" line="284"/>
|
<location filename="../qml/pages/LoginPage.qml" line="279"/>
|
||||||
<source>Security</source>
|
<source>Security</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/LoginPage.qml" line="291"/>
|
<location filename="../qml/pages/LoginPage.qml" line="286"/>
|
||||||
<source><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>.</source>
|
<source><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>.</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/LoginPage.qml" line="295"/>
|
<location filename="../qml/pages/LoginPage.qml" line="291"/>
|
||||||
<source>Do not check certificates</source>
|
<source>Do not check certificates</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/LoginPage.qml" line="296"/>
|
<location filename="../qml/pages/LoginPage.qml" line="292"/>
|
||||||
<source>Enable this option to allow selfsigned certificates</source>
|
<source>Enable this option to allow selfsigned certificates</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/LoginPage.qml" line="305"/>
|
<location filename="../qml/pages/LoginPage.qml" line="301"/>
|
||||||
<source>Allow unencrypted connections</source>
|
<source>Allow unencrypted connections</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
|
@ -321,32 +321,32 @@
|
||||||
<context>
|
<context>
|
||||||
<name>NotesApi</name>
|
<name>NotesApi</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/notesapi.cpp" line="330"/>
|
<location filename="../src/notesapi.cpp" line="333"/>
|
||||||
<source>No error</source>
|
<source>No error</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/notesapi.cpp" line="333"/>
|
<location filename="../src/notesapi.cpp" line="336"/>
|
||||||
<source>No network connection available</source>
|
<source>No network connection available</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/notesapi.cpp" line="336"/>
|
<location filename="../src/notesapi.cpp" line="339"/>
|
||||||
<source>Failed to communicate with the Nextcloud server</source>
|
<source>Failed to communicate with the Nextcloud server</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/notesapi.cpp" line="339"/>
|
<location filename="../src/notesapi.cpp" line="342"/>
|
||||||
<source>An error occured while establishing an encrypted connection</source>
|
<source>An error occured while establishing an encrypted connection</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/notesapi.cpp" line="342"/>
|
<location filename="../src/notesapi.cpp" line="345"/>
|
||||||
<source>Could not authenticate to the Nextcloud instance</source>
|
<source>Could not authenticate to the Nextcloud instance</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/notesapi.cpp" line="345"/>
|
<location filename="../src/notesapi.cpp" line="348"/>
|
||||||
<source>Unknown error</source>
|
<source>Unknown error</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
|
@ -503,152 +503,152 @@
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/SettingsPage.qml" line="100"/>
|
<location filename="../qml/pages/SettingsPage.qml" line="99"/>
|
||||||
<source>Synchronization</source>
|
<source>Synchronization</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/SettingsPage.qml" line="104"/>
|
<location filename="../qml/pages/SettingsPage.qml" line="103"/>
|
||||||
<source>Auto-Sync</source>
|
<source>Auto-Sync</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/SettingsPage.qml" line="105"/>
|
<location filename="../qml/pages/SettingsPage.qml" line="104"/>
|
||||||
<source>Periodically pull notes from the server</source>
|
<source>Periodically pull notes from the server</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/SettingsPage.qml" line="112"/>
|
<location filename="../qml/pages/SettingsPage.qml" line="111"/>
|
||||||
<source>Disabled</source>
|
<source>Disabled</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/SettingsPage.qml" line="112"/>
|
<location filename="../qml/pages/SettingsPage.qml" line="111"/>
|
||||||
<source>every</source>
|
<source>every</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/SettingsPage.qml" line="114"/>
|
<location filename="../qml/pages/SettingsPage.qml" line="113"/>
|
||||||
<source>Minutes</source>
|
<source>Minutes</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/SettingsPage.qml" line="115"/>
|
<location filename="../qml/pages/SettingsPage.qml" line="114"/>
|
||||||
<source>Seconds</source>
|
<source>Seconds</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/SettingsPage.qml" line="136"/>
|
<location filename="../qml/pages/SettingsPage.qml" line="135"/>
|
||||||
<source>The Answer is 42</source>
|
<source>The Answer is 42</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/SettingsPage.qml" line="137"/>
|
<location filename="../qml/pages/SettingsPage.qml" line="136"/>
|
||||||
<source>Congratulation you found the Answer to the Ultimate Question of Life, The Universe, and Everything!</source>
|
<source>Congratulation you found the Answer to the Ultimate Question of Life, The Universe, and Everything!</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/SettingsPage.qml" line="145"/>
|
<location filename="../qml/pages/SettingsPage.qml" line="144"/>
|
||||||
<source>Appearance</source>
|
<source>Appearance</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/SettingsPage.qml" line="153"/>
|
<location filename="../qml/pages/SettingsPage.qml" line="152"/>
|
||||||
<source>No sorting</source>
|
<source>No sorting</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/SettingsPage.qml" line="177"/>
|
<location filename="../qml/pages/SettingsPage.qml" line="176"/>
|
||||||
<source>Favorites on top</source>
|
<source>Favorites on top</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/SettingsPage.qml" line="178"/>
|
<location filename="../qml/pages/SettingsPage.qml" line="177"/>
|
||||||
<source>Show notes marked as favorite above the others</source>
|
<source>Show notes marked as favorite above the others</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/SettingsPage.qml" line="216"/>
|
<location filename="../qml/pages/SettingsPage.qml" line="215"/>
|
||||||
<source>Reset</source>
|
<source>Reset</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/SettingsPage.qml" line="219"/>
|
<location filename="../qml/pages/SettingsPage.qml" line="218"/>
|
||||||
<source>Reset app settings</source>
|
<source>Reset app settings</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/SettingsPage.qml" line="229"/>
|
<location filename="../qml/pages/SettingsPage.qml" line="228"/>
|
||||||
<source>Resetting the app wipes all application data from the device! This includes offline synced notes, app settings and accounts.</source>
|
<source>Resetting the app wipes all application data from the device! This includes offline synced notes, app settings and accounts.</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/SettingsPage.qml" line="150"/>
|
<location filename="../qml/pages/SettingsPage.qml" line="149"/>
|
||||||
<source>Last edited</source>
|
<source>Last edited</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/SettingsPage.qml" line="151"/>
|
<location filename="../qml/pages/SettingsPage.qml" line="150"/>
|
||||||
<source>Category</source>
|
<source>Category</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/SettingsPage.qml" line="152"/>
|
<location filename="../qml/pages/SettingsPage.qml" line="151"/>
|
||||||
<source>Title alphabetically</source>
|
<source>Title alphabetically</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/SettingsPage.qml" line="155"/>
|
<location filename="../qml/pages/SettingsPage.qml" line="154"/>
|
||||||
<source>Sort notes by</source>
|
<source>Sort notes by</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/SettingsPage.qml" line="156"/>
|
<location filename="../qml/pages/SettingsPage.qml" line="155"/>
|
||||||
<source>This will also change how the notes are grouped</source>
|
<source>This will also change how the notes are grouped</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/SettingsPage.qml" line="183"/>
|
<location filename="../qml/pages/SettingsPage.qml" line="182"/>
|
||||||
<source>Show separator</source>
|
<source>Show separator</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/SettingsPage.qml" line="184"/>
|
<location filename="../qml/pages/SettingsPage.qml" line="183"/>
|
||||||
<source>Show a separator line between the notes</source>
|
<source>Show a separator line between the notes</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/SettingsPage.qml" line="194"/>
|
<location filename="../qml/pages/SettingsPage.qml" line="193"/>
|
||||||
<source>lines</source>
|
<source>lines</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/SettingsPage.qml" line="195"/>
|
<location filename="../qml/pages/SettingsPage.qml" line="194"/>
|
||||||
<source>Number of lines in the preview</source>
|
<source>Number of lines in the preview</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/SettingsPage.qml" line="200"/>
|
<location filename="../qml/pages/SettingsPage.qml" line="199"/>
|
||||||
<source>Editing</source>
|
<source>Editing</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/SettingsPage.qml" line="203"/>
|
<location filename="../qml/pages/SettingsPage.qml" line="202"/>
|
||||||
<source>Monospaced font</source>
|
<source>Monospaced font</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/SettingsPage.qml" line="204"/>
|
<location filename="../qml/pages/SettingsPage.qml" line="203"/>
|
||||||
<source>Use a monospeced font to edit a note</source>
|
<source>Use a monospeced font to edit a note</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/SettingsPage.qml" line="209"/>
|
<location filename="../qml/pages/SettingsPage.qml" line="208"/>
|
||||||
<source>Capital 'X' in checkboxes</source>
|
<source>Capital 'X' in checkboxes</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../qml/pages/SettingsPage.qml" line="210"/>
|
<location filename="../qml/pages/SettingsPage.qml" line="209"/>
|
||||||
<source>For interoperability with other apps such as Joplin</source>
|
<source>For interoperability with other apps such as Joplin</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
|
@ -860,27 +860,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="121"/>
|
<location filename="../qml/harbour-nextcloudnotes.qml" line="60"/>
|
||||||
<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="122"/>
|
<location filename="../qml/harbour-nextcloudnotes.qml" line="61"/>
|
||||||
<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="123"/>
|
<location filename="../qml/harbour-nextcloudnotes.qml" line="62"/>
|
||||||
<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="137"/>
|
<location filename="../qml/harbour-nextcloudnotes.qml" line="76"/>
|
||||||
<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="130"/>
|
<location filename="../qml/harbour-nextcloudnotes.qml" line="69"/>
|
||||||
<source>File error</source>
|
<source>File error</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
|
|
Loading…
Reference in a new issue