- implemented new api and new authentication system

- some styling improvements
This commit is contained in:
Dorian Zedler 2019-12-07 15:38:35 +01:00
parent 8d0803217f
commit 4e073152e4
12 changed files with 197 additions and 525 deletions

View File

@ -44,12 +44,11 @@ class ServerConn : public QObject
private:
QString state;
// can be: loggedIn ; notLoggedIn
QString username;
QString password;
QString token;
QVariantMap senddata(QUrl serviceUrl, QUrlQuery postData, bool raw = false);
QVariantMap senddata(QUrl serviceUrl, bool raw = false);
QList<int> apiVersion = {0,2,1};
QList<int> apiVersion = {1,0,0};
FileHelper * fileHelper;
QString mDocumentsWorkPath;

View File

@ -2,91 +2,98 @@ import QtQuick 2.0
import QtQuick.Controls 2.1
import QtQuick.Controls.Material 2.1
Loader {
id: control
Loader {
id: control
property QIconSource icon: QIconSource {}
property QIconSource icon: QIconSource {}
property int fontPixelSize
property int fontPixelSize
property string text: ""
property string text
signal clicked
signal clicked
Connections {
target: icon
onNameChanged: {
control.syncProperties()
}
}
onTextChanged: {
Connections {
target: icon
onNameChanged: {
control.syncProperties()
}
function syncProperties() {
if(QtCompatiblityMode) {
control.sourceComponent = ancientToolButtonCp
control.item.text = control.text
control.item.font = control.font
if(control.fontPixelSize !== undefined) {
control.item.font.pixelSize = control.fontPixelSize
}
}
else {
control.sourceComponent = modernToolButtonCp
control.item.icon.name = control.icon.name
control.item.icon.color = control.icon.color
control.item.icon.width = control.icon.width
control.item.icon.height = control.icon.height
}
}
onItemChanged: {
control.syncProperties()
}
Component.onCompleted: {
control.syncProperties()
}
Connections {
target: control.item
onClicked: {
control.clicked()
}
}
Component {
id: ancientToolButtonCp
ToolButton {
id: tb
opacity: enabled ? 1.0 : 0.3
contentItem: Text {
text: tb.text
font: tb.font
color: app.style.style.textColor
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
}
}
}
Component {
id: modernToolButtonCp
ToolButton {
height: implicitHeight
width: implicitWidth
}
}
}
onTextChanged: {
control.syncProperties()
}
function syncProperties() {
if(control.status !== Loader.Ready) {
return
}
if(QtCompatiblityMode) {
control.item.text = control.text
control.item.font = control.font
if(control.fontPixelSize !== undefined) {
control.item.font.pixelSize = control.fontPixelSize
}
}
else {
control.item.icon.name = control.icon.name
control.item.icon.color = control.icon.color
control.item.icon.width = control.icon.width
control.item.icon.height = control.icon.height
}
}
onLoaded: {
control.syncProperties()
}
Component.onCompleted: {
if(QtCompatiblityMode) {
control.sourceComponent = ancientToolButtonCp
}
else {
control.sourceComponent = modernToolButtonCp
}
}
Connections {
target: control.item
onClicked: {
control.clicked()
}
}
Component {
id: ancientToolButtonCp
ToolButton {
id: tb
opacity: enabled ? 1.0 : 0.3
contentItem: Text {
text: tb.text
font: tb.font
color: app.style.style.textColor
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
}
}
}
Component {
id: modernToolButtonCp
ToolButton {
height: implicitHeight
width: implicitWidth
}
}
}

View File

@ -65,15 +65,4 @@ ListView {
errorCode: control.status
optionButtonFunction: control.optionButtonFunction
}
PullRefresher{
target: control
backgroundColor: app.style.style.buttonColor
pullIndicatorColor: app.style.style.textColor
preRefreshDelay: 300
refreshPosition: height * 1.3
}
}

View File

@ -54,6 +54,8 @@ FannyDataListView {
// label for the cookteam
width: parent.width
height: text!=""? undefined:0
wrapMode: Label.Wrap
font.bold: true
@ -84,14 +86,16 @@ FannyDataListView {
// label for the main dish
width: parent.width
height: text!=""? undefined:0
wrapMode: Label.Wrap
text: mainDish === "" ? "Aktuell keine Daten":mainDish
text: mainDish === ""&& mainDishLa === "" && garnish === "" ? "Aktuell keine Daten":mainDish
}
Rectangle {
width: parent.width / 10
height: mainDishVegLa.text!=""? 1:0
height: mainDishVegLa.text != "" && mainDishLa.text != "" ? 1:0
color: "grey"
}

View File

@ -1,327 +0,0 @@
/*
blueROCK - for digital rock
Copyright (C) 2019 Dorian Zedler
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import QtQuick 2.9
import QtQuick.Controls 2.1
import QtGraphicalEffects 1.0
Item {
id: control
state: "idle"
property var target // targeted ListView
property bool autoConfigureTarget: true // should the target be automaticaly be configured?
property int postRefreshDelay: 1000 // delay after reload funcion has finished
property int preRefreshDelay: 1000 // delay before reload funcion is called
property int refreshPosition: height * 1.2 // position of the item when refreshing
property int dragOutPosition: height * 1.8 // maximum drag out
property double dragRefreshPositionMultiplier: 0.5 // position of the item when starting to refresh
property color backgroundColor: "white" // color for the pre-defined background
property color pullIndicatorColor: "black" // color for the pre-defined pull indicator
//property color busyIndicatorColor: "pink" // color for the pre-defined busy indicator
readonly property double dragProgress: Math.min( userPosition / dragOutPosition, 1)
property Component background: Item {
RectangularGlow {
anchors.fill: backgroundRe
scale: 0.8 * backgroundRe.scale
cornerRadius: backgroundRe.radius
color: "black"
glowRadius: 0.001
spread: 0.2
}
Rectangle {
id: backgroundRe
anchors.fill: parent
radius: width * 0.5
color: control.backgroundColor
}
}
property Component busyIndicator: BusyIndicator { running: true }
property Component pullIndicator: Canvas {
property double drawProgress: control.dragProgress
rotation: drawProgress > control.dragRefreshPositionMultiplier ? 180:0
onDrawProgressChanged: {
requestPaint()
}
onPaint: {
var ctx = getContext("2d");
var topMargin = height * 0.1
var bottomMargin = topMargin
var rightMargin = 0
var leftMargin = 0
var arrowHeight = height - topMargin - bottomMargin
var peakHeight = arrowHeight * 0.35
var peakWidth = peakHeight
var lineWidth = 2
var progress = drawProgress * 1 / control.dragRefreshPositionMultiplier > 1 ? 1 : drawProgress * 1 / control.dragRefreshPositionMultiplier
// modify all values to math the progress
arrowHeight = arrowHeight * progress
if(progress > 0.3){
peakHeight = peakHeight * (progress - 0.3) * 1/0.7
peakWidth = peakWidth * (progress - 0.3) * 1/0.7
}
else {
peakHeight = 0
peakWidth = 0
}
// clear canvas
ctx.reset()
ctx.lineWidth = lineWidth;
ctx.strokeStyle = control.pullIndicatorColor;
// middle line
ctx.moveTo(width/2, topMargin);
ctx.lineTo(width/2, arrowHeight + topMargin);
// right line
ctx.moveTo(width/2 - lineWidth * 0.3, arrowHeight + topMargin);
ctx.lineTo(width/2 + peakWidth,arrowHeight + topMargin - peakHeight);
// left line
ctx.moveTo(width/2 + lineWidth * 0.3, arrowHeight + topMargin);
ctx.lineTo(width/2 - peakWidth,arrowHeight + topMargin - peakHeight);
ctx.stroke();
}
Behavior on rotation {
NumberAnimation {
duration: 100
}
}
}
signal refreshRequested
// internal properties
property int minimumPosition: 0
property int maximumPosition: 0
property int userPosition: 0
property int position: Math.max( minimumPosition, Math.min(maximumPosition, userPosition))
height: 50
width: height
Component.onCompleted: {
if(control.autoConfigureTarget){
target.boundsBehavior = Flickable.DragOverBounds
target.boundsMovement = Flickable.StopAtBounds
}
}
function refresh() {
control.refreshRequested()
postRefreshTimer.start()
}
anchors {
top: control.target.top
horizontalCenter: control.target.horizontalCenter
topMargin: control.position - height
}
Connections {
target: control.target
onDragEnded: {
if(userPosition >= control.dragOutPosition * control.dragRefreshPositionMultiplier){
control.state = "refreshing"
preRefreshTimer.start()
}
}
}
Loader {
id: backgroundLd
anchors.fill: parent
sourceComponent: control.background
}
Loader {
id: pullIndicatorLd
anchors.centerIn: parent
height: parent.height * 0.6
width: height
rotation: 180
sourceComponent: control.pullIndicator
}
Loader {
id: busyIndicatorLd
anchors.centerIn: parent
height: parent.height * 0.7
width: height
opacity: 0
sourceComponent: control.busyIndicator
}
Timer {
id: preRefreshTimer
interval: control.preRefreshDelay <= 0 ? 1:control.preRefreshDelay
running: false
repeat: false
onTriggered: {
control.refresh()
}
}
Timer {
id: postRefreshTimer
interval: control.postRefreshDelay <= 0 ? 1:control.postRefreshDelay
running: false
repeat: false
onTriggered: {
control.state = "hidden"
}
}
Behavior on minimumPosition {
enabled: !control.target.dragging && state !== "idle"
NumberAnimation {
duration: 100
}
}
states: [
State {
name: "idle"
PropertyChanges {
target: control
minimumPosition: userPosition > maximumPosition ? maximumPosition:userPosition
userPosition: -1 / (Math.abs( (target.verticalOvershoot > 0 ? 0:target.verticalOvershoot) * 0.001 + 0.003 ) + 1 / control.dragOutPosition * 0.001) + control.dragOutPosition // Math.abs( target.verticalOvershoot )
maximumPosition: control.dragOutPosition
}
PropertyChanges {
target: pullIndicatorLd
rotation: 0
}
},
State {
name: "refreshing"
PropertyChanges {
target: control
minimumPosition: control.refreshPosition
userPosition: 0
maximumPosition: control.refreshPosition
}
PropertyChanges {
target: pullIndicatorLd
opacity: 0
}
PropertyChanges {
target: busyIndicatorLd
opacity: 1
}
},
State {
name: "hidden"
PropertyChanges {
target: control
minimumPosition: control.refreshPosition
userPosition: 0
maximumPosition: control.refreshPosition
scale: 0
}
PropertyChanges {
target: pullIndicatorLd
opacity: 0
}
PropertyChanges {
target: busyIndicatorLd
opacity: 1
}
}
]
transitions: [
Transition {
NumberAnimation {
duration: 100
properties: "rotation, opacity"
}
},
Transition {
from: "refreshing"
to: "hidden"
PauseAnimation {
duration: 200
}
NumberAnimation {
duration: 200
properties: "scale"
}
onRunningChanged: {
if(control.state === "hidden" && !running){
control.state = "idle"
}
}
},
Transition {
from: "hidden"
to: "idle"
}
]
}

View File

@ -94,7 +94,7 @@ Page {
property: "opacity"
from: 1
to: 0
duration: 200
duration: 300
easing.type: Easing.InExpo
}
@ -186,13 +186,8 @@ Page {
text: to !== "" && model.text !== "" ? to + " | " + model.text:model.to + model.text
}
}
}
}
}
Component {
@ -202,6 +197,14 @@ Page {
}
Connections {
target: pageLoader.item
onRefresh: {
pageLoader.newSourceComponent = loadingFormComp
loadTimer.start()
}
}
Timer {
id: loadTimer
interval: 500

View File

@ -77,6 +77,14 @@ Page {
}
}
Connections {
target: pageLoader.item
onRefresh: {
pageLoader.newSource = "./LoadingForm.qml"
loadTimer.start()
}
}
Timer {
id: loadTimer
interval: 500

View File

@ -96,7 +96,7 @@ Page {
Column {
id: formCol
spacing: height * 0.01
spacing: height * 0.02
width: app.landscape() ? root.width * 0.5:root.width
height: app.landscape() ? root.height:root.height * 0.7
@ -122,14 +122,13 @@ Page {
height: formCol.rowHeight
placeholderText: "Benutzername"
Keys.onReturnPressed: login(tiuname.text, tipasswd.text, cBperm.checked)
Keys.onReturnPressed: login(tiuname.text, tipasswd.text)
}
TextField {
id: tipasswd
placeholderText: "Passwort"
Keys.onReturnPressed: login(tiuname.text, tipasswd.text, cBperm.checked)
Keys.onReturnPressed: login(tiuname.text, tipasswd.text)
height: formCol.rowHeight
@ -192,16 +191,22 @@ Page {
}
CheckDelegate {
id: cBperm
Label {
id: laStatus
anchors.horizontalCenter: parent.horizontalCenter
height: formCol.rowHeight
height: formCol.rowHeight * 0.5
width: parent.width
checked: true
fontSizeMode: Text.Fit
font.pixelSize: height
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
text: qsTr("Angemeldet bleiben")
color: "red"
text: qsTr("")
}
FancyButton {
@ -219,39 +224,27 @@ Page {
text: qsTr("Anmelden")
onClicked: root.login(tiuname.text, tipasswd.text, cBperm.checked)
}
FancyButton {
id: registerBt
anchors {
horizontalCenter: parent.horizontalCenter
left: parent.left
margins: window.width * 0.05
}
height: formCol.rowHeight
enabled: true
text: qsTr("Registrieren")
onClicked: Qt.openUrlExternally("http://www.fanny-leicht.de/j34/index.php/login?view=registration")
onClicked: root.login(tiuname.text, tipasswd.text)
}
Label {
id: laStatus
id: registerAndForgotLa
anchors.horizontalCenter: parent.horizontalCenter
height: formCol.rowHeight
verticalAlignment: Text.AlignVCenter
font.pixelSize: height * 0.3
horizontalAlignment: Text.AlignHCenter
color: "red"
text: "<html><a href=\"http://www.fanny-leicht.de/j34/index.php/login?view=registration\">Registrieren</a><br><a href=\"http://www.fanny-leicht.de/j34/index.php/login?view=reset\">Passwort vergessen?</a><br><a href=\"http://www.fanny-leicht.de/j34/index.php/login?view=remind\">Benutzername vergessen?</a>"
onLinkActivated: {
Qt.openUrlExternally(link)
}
text: qsTr("")
}
}
@ -291,7 +284,7 @@ Page {
}
}
function login(username, password, permanent){
function login(username, password){
// hide the keyboard
Qt.inputMethod.hide();
// open the busy dialog
@ -302,7 +295,7 @@ Page {
loginButton.text = "Anmelden.."
// trigger the login fucntion of the cpp backend and store the return code
var ret = serverConn.login(username, password, permanent);
var ret = serverConn.login(username, password, true);
// the request has finished
// close the busy dialog
@ -312,17 +305,10 @@ Page {
// change the text of the login button back to "Anmelden"
loginButton.text = "Anmelden"
// chekc if the login was successfull
if(ret === 200){
// if it was -> set the app to inited and set the state of the app to loggedIn
_cppAppSettings.writeSetting("init", 1);
app.is_error = false;
app.state = "loggedIn"
}
else{
// if it wasn't -> set the error label to the error short description of the retuned error code
// chekc if the login was not successfull
if(ret !== 200){
// set the error label to the error short description of the retuned error code
laStatus.text = app.getErrorInfo(ret)[1]
}
}
}

View File

@ -131,7 +131,12 @@ ApplicationWindow {
case 401:
infoLevel = 3
errorString = "Ungültige Zugangsdaten"
errorDescription = "Der Server hat den Zugang verweigert, bitte überprüfe deine Zugangsdaten und versuche es erneut"
errorDescription = "Der Server hat den Zugang verweigert, bitte überprüfe deine Zugangsdaten und versuche es erneut."
break
case 403:
infoLevel = 3
errorString = "Account nicht freigegeben"
errorDescription = "Die Anmeldedaten waren korrekt, der Account ist jedoch nicht freigegeben."
break
case 500:
infoLevel = 3

View File

@ -17,7 +17,6 @@
<file>Components/SettingsDelegate.qml</file>
<file>Components/FannyDataListView.qml</file>
<file>Components/ProgressCircle.qml</file>
<file>Components/PullRefresher.qml</file>
<file>Components/CompatibleToolButton.qml</file>
<file>Components/QIconSource.qml</file>
</qresource>

View File

@ -42,6 +42,10 @@ AppSettings::AppSettings(QObject* parent)
this->writeSetting("theme", "Light");
}
// remove remainings of the former login mechsnism
this->writeSetting("username", "");
this->writeSetting("password", "");
this->filtersFile = new QFile(path + "/fannyapp/filters.json");
}

View File

@ -53,8 +53,7 @@ ServerConn::ServerConn(QObject *parent) : QObject(parent)
if(perm == 1){
// permanent login -> restore login
this->username = pGlobalAppSettings->loadSetting("username");
this->password = pGlobalAppSettings->loadSetting("password");
this->token = pGlobalAppSettings->loadSetting("token");
this->setState("loggedIn");
}
@ -65,57 +64,54 @@ ServerConn::ServerConn(QObject *parent) : QObject(parent)
int ServerConn::login(QString username, QString password, bool permanent)
{
// add the data to the request
QUrlQuery pdata;
pdata.addQueryItem("username", username);
pdata.addQueryItem("password", password);
// send the request
QVariantMap ret = this->senddata(QUrl("https://www.fanny-leicht.de/j34/templates/g5_helium/intern/events.php"), pdata);
QVariantMap ret = this->senddata(QUrl("https://www.fanny-leicht.de/j34/index.php/component/fannysubstitutionplan?task=api_login&username=" + username + "&password=" + password));
qDebug() << ret;
if(ret["status"].toInt() == 200){
// if not 200 was returned -> user data was correct
// store username and password in the class variables
this->username = username;
this->password = password;
QJsonDocument jsonDoc = QJsonDocument::fromJson(ret["text"].toString().toUtf8());
if(permanent){
// if the user wants to say logged in, store the username and password to the settings file
pGlobalAppSettings->writeSetting("permanent", "1");
pGlobalAppSettings->writeSetting("username", username);
pGlobalAppSettings->writeSetting("password", password);
if(jsonDoc.toVariant().toMap()["result"].toInt() == 200) {
this->token = jsonDoc.toVariant().toMap()["data"].toMap()["token"].toString();
if(permanent){
// if the user wants to say logged in, store the username and password to the settings file
pGlobalAppSettings->writeSetting("permanent", "1");
pGlobalAppSettings->writeSetting("token", this->token);
pGlobalAppSettings->writeSetting("password", password);
}
// set state to loggedIn
this->setState("loggedIn");
qDebug() << "+----- logged in -----+";
// return success
return 200;
}
else {
ret["status"] = jsonDoc.toVariant().toMap()["result"].toInt();
}
// set state to loggedIn
this->setState("loggedIn");
qDebug() << "+----- logged in -----+";
// return success
return(200);
}
else {
// if not 200 was returned -> error -> return the return code
this->setState("notLoggedIn");
// -> reset the stored credentinals
pGlobalAppSettings->writeSetting("permanent", "0");
pGlobalAppSettings->writeSetting("username", "");
pGlobalAppSettings->writeSetting("password", "");
return(ret["status"].toInt());
}
// if not 200 was returned -> error -> return the return code
this->setState("notLoggedIn");
// -> reset the stored credentinals
pGlobalAppSettings->writeSetting("permanent", "0");
pGlobalAppSettings->writeSetting("token", "");
return ret["status"].toInt();
}
int ServerConn::logout()
{
// reset the data stored in the class
this->username = "";
this->password = "";
this->token = "";
// reset the data stored in the settings
pGlobalAppSettings->writeSetting("permanent", "0");
pGlobalAppSettings->writeSetting("username", "");
pGlobalAppSettings->writeSetting("password", "");
pGlobalAppSettings->writeSetting("token", "");
this->setState("notLoggedIn");
@ -134,13 +130,13 @@ int ServerConn::getEvents(QString day)
// add the data to the request
QUrlQuery pdata;
pdata.addQueryItem("username", this->username);
pdata.addQueryItem("password", this->password);
pdata.addQueryItem("token", this->token);
pdata.addQueryItem("mode", pGlobalAppSettings->loadSetting("teacherMode") == "true" ? "1":"0");
pdata.addQueryItem("day", day);
// send the request
QVariantMap ret = this->senddata(QUrl("https://www.fanny-leicht.de/j34/templates/g5_helium/intern/events.php"), pdata);
QVariantMap ret = this->senddata(QUrl("https://www.fanny-leicht.de/j34/index.php/component/fannysubstitutionplan?task=api_getData&token=" + token
+ "&mode=" + (pGlobalAppSettings->loadSetting("teacherMode") == "true" ? "1":"0") + "&day=" + day));
if(ret["status"].toInt() != 200){
// if the request didn't result in a success, clear the old events, as they are probaply incorrect and return the error code
@ -280,14 +276,14 @@ int ServerConn::openEventPdf(QString day) {
// add the data to the request
QUrlQuery pdata;
pdata.addQueryItem("username", this->username);
pdata.addQueryItem("password", this->password);
pdata.addQueryItem("token", this->token);
pdata.addQueryItem("mode", pGlobalAppSettings->loadSetting("teacherMode") == "true" ? "1":"0");
pdata.addQueryItem("day", day);
pdata.addQueryItem("asPdf", "true");
// send the request
QVariantMap ret = this->senddata(QUrl("https://www.fanny-leicht.de/j34/templates/g5_helium/intern/events.php"), pdata, true);
QVariantMap ret = this->senddata(QUrl("https://www.fanny-leicht.de/j34/index.php/component/fannysubstitutionplan?task=api_getData&token=" + token
+ "&mode=" + (pGlobalAppSettings->loadSetting("teacherMode") == "true" ? "1":"0") + "&day=" + day + "&asPdf=true"), true);
if(ret["status"].toInt() != 200){
// if the request didn't result in a success, clear the old events, as they are probaply incorrect and return the error code
@ -327,9 +323,8 @@ int ServerConn::getFoodPlan()
url.append("&fields[]="+foodplanDataKey);
}
QUrlQuery pdata;
// send the request to the server
QVariantMap ret = this->senddata(QUrl(url), pdata);
QVariantMap ret = this->senddata(QUrl(url));
if(ret["status"].toInt() != 200){
// if the request didn't result in a success, return the error code
@ -404,7 +399,7 @@ int ServerConn::getFoodPlan()
return(200);
}
QVariantMap ServerConn::senddata(QUrl serviceUrl, QUrlQuery pdata, bool raw)
QVariantMap ServerConn::senddata(QUrl serviceUrl, bool raw)
{
// create network manager
QNetworkAccessManager * networkManager = new QNetworkAccessManager();
@ -438,11 +433,11 @@ QVariantMap ServerConn::senddata(QUrl serviceUrl, QUrlQuery pdata, bool raw)
timer.start(10000);
this->updateDownloadProgress(0, 1);
reply = networkManager->post(request, pdata.toString(QUrl::FullyEncoded).toUtf8());
reply = networkManager->get(request);
connect(reply, &QNetworkReply::sslErrors, this, [=](){ reply->ignoreSslErrors(); });
connect(reply, SIGNAL(downloadProgress(qint64, qint64)),
this, SLOT(updateDownloadProgress(qint64, qint64)));
this, SLOT(updateDownloadProgress(qint64, qint64)));
// start the loop
loop.exec();