diff --git a/Gui/AddNewObject.qml b/Gui/AddNewObject.qml
index d3ffd6a..1641e18 100644
--- a/Gui/AddNewObject.qml
+++ b/Gui/AddNewObject.qml
@@ -11,6 +11,7 @@ GridLayout
Layout.fillHeight: true
rowSpacing: 9
+
//// New grid row
Label
@@ -218,7 +219,9 @@ GridLayout
function checkObjectField()
{
+
return street.text.trim() && houseno.text.trim() &&
+
(postcode.editText.trim() || postcode.currentText.trim()) &&
(city.editText.trim() || city.currentText.trim()) &&
cleaningproducts.text.trim()
diff --git a/Gui/BackupSettings.qml b/Gui/BackupSettings.qml
new file mode 100644
index 0000000..31a6076
--- /dev/null
+++ b/Gui/BackupSettings.qml
@@ -0,0 +1,115 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import QtQuick.Dialogs
+import QtCore
+import "../js/qmldict.js" as JsLib
+
+
+
+GridLayout
+{
+ anchors.fill: parent
+ anchors.topMargin: 150
+ columns: 2
+ Label
+ {
+ Layout.columnSpan: 2
+ Layout.alignment: Qt.AlignHCenter
+ text: qsTr("Sicherung")
+ font.pixelSize: 35
+ }
+
+ Label
+ {
+ text: qsTr("Konfiguration")
+ Layout.alignment: Qt.AlignRight
+
+ }
+ Button
+ {
+ id: saveConfig
+ text: qsTr("Jetzt sichern!")
+ onClicked: dialog.open()
+ }
+ Label
+ {
+ text: qsTr("Verschlüsselung")
+ Layout.alignment: Qt.AlignRight
+ }
+ Button
+ {
+ id: saveEncryption
+ text: qsTr("Jetzt sichern!")
+ onClicked: apploader.item.recoverEnc.open()
+ }
+ Item
+ {
+ id: spacer
+ Layout.fillHeight: true
+ }
+ Dialog
+ {
+ anchors.centerIn: parent
+ id: dialog
+ title: "Title"
+ standardButtons: Dialog.Apply | Dialog.Cancel
+
+ onApplied:
+ {
+
+ if (configPwd.text === repeatConfigPwd.text)
+ {
+
+ saveConfigFile.open()
+ }
+ else
+ {
+ configPwd.text = ""
+ configPwd.placeholderText = qsTr("Passwort stimmt nicht überein")
+ configPwd.placeholderTextColor = "red"
+ repeatConfigPwd.placeholderText = qsTr("")
+ repeatConfigPwd.text = ""
+ }
+ }
+ onRejected: console.log("Cancel clicked")
+ GridLayout
+ {
+ id: gridPw
+ columns: 2
+ Label
+ {
+ text: qsTr("Passwort eingeben")
+ }
+ TextField
+ {
+
+ id: configPwd
+ placeholderText: qsTr("Sicherungspasswort festlegen")
+ }
+ Label
+ {
+ text: qsTr("Passwort wiederholen")
+ }
+ TextField
+ {
+ property string name: "password"
+ id: repeatConfigPwd
+ placeholderText: qsTr("Sicherungspasswort wiederholen")
+ }
+
+ }
+ }
+ FileDialog
+ {
+ id: saveConfigFile
+ fileMode: FileDialog.SaveFile
+ nameFilters: ["PYQCRM Recovery files (*.pyqrec)"]
+ currentFolder: StandardPaths.standardLocations(StandardPaths.DocumentsLocation)[0]
+ onAccepted:
+ {
+ var pw = JsLib.parseForm(gridPw)
+ config.backupConfig(saveConfigFile.currentFile, pw["password"])
+ }
+ }
+}
diff --git a/Gui/firststart.qml b/Gui/Firststart.qml
similarity index 89%
rename from Gui/firststart.qml
rename to Gui/Firststart.qml
index 38544a8..031d688 100644
--- a/Gui/firststart.qml
+++ b/Gui/Firststart.qml
@@ -11,7 +11,9 @@ Item
{
property string recpass: ""
property bool adminAvailable: true
+ property alias recoverEnc: recoveryPaswordDialog
+ id: firstStartItem
anchors.fill: parent
StackView
{
@@ -87,6 +89,7 @@ Item
Dialog
{
+
id: recoveryPaswordDialog
modal: true
title: qsTr("Wiederherstellung")
@@ -115,6 +118,19 @@ Item
implicitWidth: 300
placeholderText: qsTr("Hier Wiederherstellungspasswort eingeben")
}
+ Label
+ {
+ text: qsTr("Wiederherstellungspasswort eingeben: ")
+ }
+
+ TextField
+ {
+ id: repeatRecoveryPaswordInput
+ text: ""
+ echoMode: TextInput.Password
+ implicitWidth: 300
+ placeholderText: qsTr("Hier Wiederherstellungspasswort eingeben")
+ }
}
}
}
diff --git a/Gui/ObjectAddOnContactPerson.qml b/Gui/ObjectAddOnContactPerson.qml
index fa5755c..e999b81 100644
--- a/Gui/ObjectAddOnContactPerson.qml
+++ b/Gui/ObjectAddOnContactPerson.qml
@@ -269,8 +269,10 @@ GridLayout
highlightFollowsCurrentItem: false
onActiveFocusChanged: if(!focus) currentIndex = -1
delegate: Item
+
{
width: contactView.width
+
height: 15
MouseArea
{
@@ -281,17 +283,7 @@ GridLayout
contactView.highlightFollowsCurrentItem = true
}
}
- MouseArea
- {
- id: clickedRow
- anchors.fill: parent
- onClicked:
- {
- //var currentIndex = index
- console.log(index)
- console.log(contactView.currentItem.y)
- }
- }
+
Row
{
diff --git a/Gui/PyqcrmConf.qml b/Gui/PyqcrmConf.qml
index 2527b3e..b37913c 100644
--- a/Gui/PyqcrmConf.qml
+++ b/Gui/PyqcrmConf.qml
@@ -21,6 +21,12 @@ Item
{
text: qsTr("Das Unternehmen")
}
+
+ TabButton
+ {
+ text: qsTr("Sicherung")
+ }
+
}
StackLayout
@@ -55,6 +61,17 @@ Item
anchors.fill: parent
}
}
+
+ Item
+ {
+ id: backup
+ BackupSettings
+ {
+ id: backupSettings
+ anchors.fill: parent
+ }
+ }
+
}
RowLayout
diff --git a/Gui/main.qml b/Gui/main.qml
index 2e85370..3d43405 100644
--- a/Gui/main.qml
+++ b/Gui/main.qml
@@ -12,6 +12,8 @@ ApplicationWindow
visible: true
title: "PYQCRM"
property string confile: ""
+ property alias settingsFileDialog: settingsFiledialog
+
TopBar
{
@@ -80,17 +82,18 @@ ApplicationWindow
anchors.centerIn: parent
standardButtons: Dialog.Yes | Dialog.No
onAccepted: settingsFiledialog.open()
- onRejected: appLoader.source= "firststart.qml"
+ onRejected: appLoader.source= "Firststart.qml"
title: qsTr("Einstellungen importieren")
}
FileDialog
{
+
id: settingsFiledialog
title: qsTr("PYQCRM Einstellungen")
currentFolder: StandardPaths.standardLocations(StandardPaths.DocumentsLocation)[0]
modality: "ApplicationModal"
- nameFilters: [qsTr("PYQCRM Einstellungen (*.pyqcrm)")]
+ nameFilters: [qsTr("PYQCRM Einstellungen (*.pyqrec)")]
onAccepted:
{
exportFilePassword.open()
diff --git a/enc_key_backup.txt b/enc_key_backup.txt
new file mode 100644
index 0000000..a4915d4
--- /dev/null
+++ b/enc_key_backup.txt
@@ -0,0 +1 @@
+Lj30yFOP7hJmY5Cub1Go8fJz0UE+Zyo9cEqNxfY23Sc=
diff --git a/lib/ConfigLoader.py b/lib/ConfigLoader.py
index a3bda34..b56ce66 100644
--- a/lib/ConfigLoader.py
+++ b/lib/ConfigLoader.py
@@ -110,6 +110,12 @@ class ConfigLoader(QObject):
self.backupEncryptionKey.emit()
return admin
+ """###################################################"""
+ """ """
+ """ TODO: Rename method and rename self.__encrypt_key """
+ """ """
+ """###################################################"""
+
@Slot(str, str)
def saveRecoveryKey(self, recovery_file, recovery_password):
# print(f"In {__file__} file, saveRecoveryKey()")
@@ -131,30 +137,39 @@ class ConfigLoader(QObject):
@Slot(str, str)
def getRecoveryKey(self, recovery_file, recovery_password):
- # print(f"In {__file__} file, getRecoveryKey()")
- local = False
rec_file = urlparse(recovery_file)
rec_file = rec_file.path
if os.name == "nt":
rec_file = rec_file [1:]
try:
- with open(rec_file, "r") as f:
- rf = f.read()
- rf = Vermasseln().entschluesseln(rf, local)
- ek = rf[128:]
- ek = ek[:-32]
- password = rf[:128]
- salt = rf[-32:]
- ok = self.__checkRecoveryPassword(recovery_password, password, salt)
- if ok:
- self.__setEncryptionKey(ek)
- self.configurationReady.emit()
- else:
- self.__invalidateEncryptionKey()
- self.invalidEncryptionKey.emit()
+ ek = self.__parseImport(rec_file, recovery_password)
+
+ if ek:
+ self.__setEncryptionKey(ek)
+ self.configurationReady.emit()
+ else:
+ self.__invalidateEncryptionKey()
+ self.invalidEncryptionKey.emit()
except Exception as e:
print(str(e))
+ def __parseImport(self, rec_file, recovery_password):
+ local = False
+ with open(rec_file, "r") as f:
+
+ rf = f.read()
+ rf = Vermasseln().entschluesseln(rf, local)
+ ek = rf[128:]
+ ek = ek[:-32]
+ password = rf[:128]
+ salt = rf[-32:]
+ ok = self.__checkRecoveryPassword(recovery_password, password, salt)
+ if ok:
+ return ek
+ else:
+ return None
+
+
def __invalidateEncryptionKey(self):
# print(f"In {__file__} file, __invalidateEncryptionKey()")
self.__config['pyqcrm']['ENCRYPTION_KEY_VALID'] = 'No'
@@ -173,13 +188,25 @@ class ConfigLoader(QObject):
@Slot(str, str) # todo: non local encryption
def importConfig(self, confile, password):
- # print(f"In {__file__} file, importConfig()")
confile = urlparse(confile)
confile = confile.path
-
if os.name == "nt":
confile = confile[1:]
- shutil.copyfile(confile, self.config_dir+ '/pyqcrm.toml')
+ try:
+ ek = self.__parseImport(confile, password)
+ if ek:
+ self.__config = toml.loads(ek)
+ self.__saveConfig()
+ self.configurationReady.emit()
+ else:
+ self.invalidEncryptionKey.emit()
+ except Exception as e:
+ print(str(e))
+
+
+
+
+
def __configLoad(self):
# print(f"In {__file__} file, __configLoad()")
@@ -195,6 +222,7 @@ class ConfigLoader(QObject):
except Exception as e:
print(str(e))
+
def getConfig(self):
# print(f"In {__file__} file, getConfig()")
# print(self.__config)
@@ -212,3 +240,8 @@ class ConfigLoader(QObject):
self.__config['pyqcrm']['ENCRYPTION_KEY'] = enc_key
self.__saveConfig()
+ @Slot(str, str)
+ def backupConfig(self, filename, password):
+ self.__encrypt_key = toml.dumps(self.getConfig())
+ self.saveRecoveryKey(filename, password)
+
diff --git a/lib/Vermasseln.py b/lib/Vermasseln.py
index 72e3190..67570cb 100644
--- a/lib/Vermasseln.py
+++ b/lib/Vermasseln.py
@@ -53,7 +53,6 @@ class Vermasseln:
hash_pw = (salt + password).encode("utf-8")
h_obj = SHA3_512.new(hash_pw)
password = salt + "$" + h_obj.hexdigest()
-
return password
diff --git a/qml.qrc b/qml.qrc
index 7996b66..a524421 100644
--- a/qml.qrc
+++ b/qml.qrc
@@ -10,7 +10,6 @@
Gui/CustomerTable.qml
Gui/Dashboard.qml
Gui/EmployeeTable.qml
- Gui/firststart.qml
Gui/main.qml
Gui/SearchBar.qml
js/qmldict.js
@@ -44,5 +43,7 @@
Gui/UsersPage.qml
Gui/PyqcrmConf.qml
Gui/CompanyConf.qml
+ Gui/Firststart.qml
+ Gui/BackupSettings.qml