diff --git a/src/server/Database.cpp b/src/server/Database.cpp index cf5070aba..00b5ed644 100644 --- a/src/server/Database.cpp +++ b/src/server/Database.cpp @@ -1,168 +1,255 @@ /* GCompris - Database.cpp * * Copyright (C) 2016 Johnny Jazeix * * Authors: * Johnny Jazeix * * 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 . */ #include #include #include #include #include #include #include #include "UserData.h" #include "Database.h" #include "GroupData.h" + +#define CREATE_TABLE_USERS \ + "CREATE TABLE users (user_name TEXT PRIMARY KEY NOT NULL, avatar TEXT); " #define CREATE_TABLE_GROUPS \ "CREATE TABLE groups (group_name TEXT PRIMARY KEY NOT NULL, description TEXT); " +#define CREATE_TABLE_USERGROUP \ + "CREATE TABLE group_users(user_name TEXT NOT NULL, group_name TEXT NOT NULL)" Database* Database::_instance = 0; Database::Database() { } Database::~Database() { } Database* Database::getInstance() { if (!_instance) _instance = new Database; return _instance; } bool Database::addGroup(const QString &groupName, const QString& description, const QStringList& users) { bool groupAdded = false; QSqlDatabase dbConnection = QSqlDatabase::database(); QSqlQuery query(dbConnection); // add group to db only if it has not been added before query.prepare("SELECT group_name FROM groups WHERE group_name=:groupName"); query.bindValue(":groupName", groupName); query.exec(); - if(query.next()){ + if(query.next()) { qDebug()<< "group "<< groupName << " already exists"; return false; } // since the group does not exist ,create the new group and add description and users to it query.prepare("INSERT INTO groups (group_name, description) VALUES (:groupName,:description)"); query.bindValue(":groupName", groupName); query.bindValue(":description",description); groupAdded = query.exec(); - if(groupAdded){ + if(groupAdded) { //add users to the group + for(const auto &user: users) { + addUserToGroup(groupName, user); + } } - else{ - + else qDebug()<<"group could not be added " << query.lastError(); - } + return groupAdded; } + bool Database::deleteGroup(const QString &groupName) { bool groupDeleted = false; QSqlDatabase dbConnection = QSqlDatabase::database(); QSqlQuery query(dbConnection); query.prepare("DELETE FROM groups WHERE group_name=:gname"); query.bindValue(":gname",groupName); - if(query.exec()){ - groupDeleted = true; - // query.prepare("DELETE FROM group_users WHERE group_name=:gname"); - // query.bindValue(":gname",groupName); - // if(query.exec()) - // groupDeleted = true; + if(query.exec()) { + query.prepare("DELETE FROM group_users WHERE group_name=:gname"); + query.bindValue(":gname",groupName); + if(query.exec()) + groupDeleted = true; } return groupDeleted; } +bool Database::addUserToGroup(const QString& group, const QString& user) +{ + // insert in table group_users + // add (user, group) to db only if they don't exist + bool userAdded = false; + QSqlDatabase dbConnection = QSqlDatabase::database(); + QSqlQuery query(dbConnection); + query.prepare("SELECT * FROM group_users WHERE user_name=:user and group_name=:group"); + query.bindValue(":user",user); + query.bindValue(":group",group); + query.exec(); + if(query.next()) { + qDebug() << "user " << user << "already exists in group " << group; + return false; + } + query.prepare("INSERT INTO group_users (user_name, group_name) values(:user,:group)"); + query.bindValue(":user",user); + query.bindValue(":group",group); + userAdded = query.exec(); + if(!userAdded) { + qDebug() << "user could not be added "<< query.lastError(); + } + return userAdded; + +} + + +bool Database::addUser(const QString& name, const QString &avatar, const QStringList& groups) +{ + // check whether user already exists before adding to database + bool userAdded = false; + QSqlDatabase dbConnection = QSqlDatabase::database(); + QSqlQuery query(dbConnection); + query.prepare("SELECT user_name FROM users WHERE user_name=:name"); + query.bindValue(":name", name); + query.exec(); + if(query.next()) { + qDebug() << "user " << name << "already exists"; + return false; + } + query.prepare("INSERT INTO users (user_name, avatar) VALUES(:name,:avatar)"); + query.bindValue(":name", name); + query.bindValue(":avatar", avatar); + userAdded = query.exec(); + if(userAdded) { + for (const auto &group:groups) { + addUserToGroup(group, name); + } + } + else + qDebug()<< query.lastError(); + + return userAdded; +} + + void Database::retrieveAllExistingGroups(QList &allGroups) { QSqlDatabase dbConnection = QSqlDatabase::database(); // Don't add twice the same login QSqlQuery query(dbConnection); query.prepare("SELECT * FROM groups"); query.exec(); const int nameIndex = query.record().indexOf("group_name"); const int descriptionIndex = query.record().indexOf("description"); while(query.next()) { GroupData *g = new GroupData(); g->setName(query.value(nameIndex).toString()); g->setDescription(query.value(descriptionIndex).toString()); allGroups.push_back(g); } } +QMultiMap Database::retrieveGroupUsers() +{ + QSqlDatabase dbConnection = QSqlDatabase::database(); + QSqlQuery query(dbConnection); + query.prepare("SELECT * FROM group_users"); + query.exec(); + int userIndex = query.record().indexOf("user_name"); + int groupIndex = query.record().indexOf("group_name"); + QMultiMap groupUsers; + while(query.next()) { + groupUsers.insert(query.value(groupIndex).toString(),query.value(userIndex).toString()); + } + return groupUsers; + +} void Database::retrieveAllExistingUsers(QList &allUsers) { QSqlDatabase dbConnection = QSqlDatabase::database(); - - // Don't add twice the same login QSqlQuery query(dbConnection); query.prepare("SELECT * FROM users"); query.exec(); - const int nameIndex = query.record().indexOf("login"); + const int nameIndex = query.record().indexOf("user_name"); const int avatarIndex = query.record().indexOf("avatar"); while(query.next()) { UserData *u = new UserData(); u->setName(query.value(nameIndex).toString()); u->setAvatar(query.value(avatarIndex).toString()); allUsers.push_back(u); } } void createDatabase(const QString &path) { QSqlDatabase dbConnection = QSqlDatabase::database(); - QSqlQuery query(dbConnection); + + if(query.exec(CREATE_TABLE_USERS)) + qDebug()<< "created table users"; + else + qDebug() << query.lastError(); + + if(query.exec(CREATE_TABLE_GROUPS)) qDebug()<< "created table groups"; - else{ - qDebug() <<"failed"; + else qDebug() << query.lastError(); - } + + if(query.exec(CREATE_TABLE_USERGROUP)) + qDebug()<< "created table group_users"; + else + qDebug() << query.lastError(); + + } void Database::init() { QDir databasePath; QString path = databasePath.currentPath()+"/gcompris-qt.db"; // todo set cache/data path instead of current folder QSqlDatabase dbConnection = QSqlDatabase::addDatabase("QSQLITE"); dbConnection.setDatabaseName(path); QFileInfo fileInfo(path); if(!fileInfo.exists()) { if (!dbConnection.open()) { qDebug() << "Error: connection with database fail"; } createDatabase(path); } if (!dbConnection.open()) { qDebug() << "Error: connection with database fail"; } } diff --git a/src/server/Database.h b/src/server/Database.h index e3d6db456..b93245f4d 100644 --- a/src/server/Database.h +++ b/src/server/Database.h @@ -1,57 +1,64 @@ /* GCompris - Database.h * * Copyright (C) 2016 Johnny Jazeix * * Authors: * Johnny Jazeix * * 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 . */ #ifndef DATABASE_H #define DATABASE_H #include #include +#include +#include class UserData; class GroupData; class QSqlError; class Database : public QObject { Q_OBJECT private: Database(); static Database* _instance; // singleton instance public: /** * Registers Database singleton in the QML engine. */ static void init(); static QObject *systeminfoProvider(QQmlEngine *engine, QJSEngine *scriptEngine); static Database* getInstance(); ~Database(); bool addGroup(const QString &groupName, const QString& description = QString(), const QStringList& users=QStringList()); bool deleteGroup(const QString& groupName); void retrieveAllExistingUsers(QList &allUsers); void retrieveAllExistingGroups(QList &allGroups); + bool addUser(const QString &name, const QString &avatar = "", const QStringList& groups=QStringList()); + bool addUserToGroup(const QString& group, const QString& user); + + QMultiMap retrieveGroupUsers(); + }; #endif diff --git a/src/server/MessageHandler.cpp b/src/server/MessageHandler.cpp index f15e78adb..a6db29d57 100644 --- a/src/server/MessageHandler.cpp +++ b/src/server/MessageHandler.cpp @@ -1,257 +1,312 @@ /* GComprisServer - MessageHandler.cpp * * Copyright (C) 2016 Johnny Jazeix * * Authors: * Johnny Jazeix * * 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 . */ #include #include "Server.h" #include "Database.h" #include "MessageHandler.h" +#include +#include MessageHandler* MessageHandler::_instance = 0; MessageHandler::MessageHandler() { Server &server = *Server::getInstance(); connect(&server, &Server::loginReceived, this, &MessageHandler::onLoginReceived); connect(&server, &Server::activityDataReceived, this, &MessageHandler::onActivityDataReceived); connect(&server, &Server::newClientReceived, this, &MessageHandler::onNewClientReceived); connect(&server, &Server::clientDisconnected, this, &MessageHandler::onClientDisconnected); // retrieve all the existing users and groups + + QList users; + Database::getInstance()->retrieveAllExistingUsers(users); + for(auto it=users.begin(); it!= users.end(); it++) { + m_users.push_back((QObject*)(*it)); + } + QList groups; Database::getInstance()->retrieveAllExistingGroups(groups); - for(auto it=groups.begin(); it!= groups.end(); it++){ + for(auto it=groups.begin(); it!= groups.end(); it++) { m_groups.push_back((QObject*)(*it)); } - emit newUsers(); + // add users to their respective groups + QMultiMap groupUsers = Database::getInstance()->retrieveGroupUsers(); + QMapIterator itr(groupUsers); + while(itr.hasNext()) { + itr.next(); + GroupData* grp = getGroup(itr.key()); + QList values = groupUsers.values(itr.key()); + for(const auto &user: values) { + UserData* usr = getUser(user); + if(grp && usr) { + + grp->addUser(usr); + usr->addGroup(grp); + } + } + } } MessageHandler* MessageHandler::getInstance() { if (!_instance) _instance = new MessageHandler; return _instance; } QObject *MessageHandler::systeminfoProvider(QQmlEngine *engine, QJSEngine *scriptEngine) { Q_UNUSED(engine) Q_UNUSED(scriptEngine) return getInstance(); } void MessageHandler::init() { getInstance(); qmlRegisterType(); qmlRegisterType(); qmlRegisterSingletonType("GCompris", 1, 0, "MessageHandler", systeminfoProvider); } GroupData *MessageHandler::createGroup(const QString &newGroup,const QString &description, const QStringList& users) { //1. add group to database //2. make a new a group and add it to m_groups; - if(Database::getInstance()->addGroup(newGroup, description,users)){ + if(Database::getInstance()->addGroup(newGroup, description,users)) { GroupData *c = new GroupData(); c->setName(newGroup); c->setDescription(description); m_groups.push_back((QObject*)c); + for(const auto &user: users) { + UserData* usr = getUser(user); + if(usr) { + c->addUser(usr); + usr->addGroup(c); + } + } qDebug() << "size of the list after adding the new group is " << m_groups.length(); emit newGroups(); return c; } return nullptr; } void MessageHandler::deleteGroup(const QString &groupName) { //delete from database if(Database::getInstance()->deleteGroup(groupName)){ GroupData *c = getGroup(groupName); qDebug() << c; m_groups.removeAll(c); delete c; emit newGroups(); } else{ qDebug() << "could not delete the group from database"; } } UserData *MessageHandler::createUser(const QString &newUser, const QString &avatar, const QStringList &groups) { // Add the user in the database -/* if(Database::getInstance()->addUser(newUser, avatar, groups)){ + if(Database::getInstance()->addUser(newUser, avatar, groups)) { qDebug() << "createUser '" << newUser << "' in groups " << groups; UserData *u = new UserData(); u->setName(newUser); u->setAvatar(avatar); for(const QString &aGroup: groups) { GroupData *group = getGroup(aGroup); - if(group){ + if(group) { group->addUser(u); u->addGroup(group); } } m_users.push_back((QObject*)u); emit newUsers(); return u; } else { qDebug() << "Error while creating user " << newUser; } - return nullptr;*/ + return nullptr; } +void MessageHandler::addUserToGroup(const QStringList &groups, const QStringList &users) +{ + // 1. add user to group -In database; + // 2. associate users with groups; + for(const auto &user: users) { + + UserData* usr = getUser(user); + for (const auto &group: groups) { + + if(Database::getInstance()->addUserToGroup(group,user)) { + + GroupData* grp = getGroup(group); + + if(grp && usr) { + grp->addUser(usr); + usr->addGroup(grp); + } + } + } + } + +} + + UserData *MessageHandler::updateUser(const QString &oldUser, const QString &newUser, const QString &avatar, const QStringList &groups) { UserData *user = getUser(oldUser); if (user) { user->setName(newUser); // for each group, remove the user if not in the new groups and add it in the new ones removeUserFromAllGroups(user); for(const QString &aGroup: groups) { GroupData *group = getGroup(aGroup); group->addUser(user); } emit newUsers(); } return user; } UserData *MessageHandler::getUser(const QString &userName) { for (QObject *oUser: m_users) { UserData *user = (UserData *) oUser; if (user->getName() == userName) { return user; } } return nullptr; } GroupData *MessageHandler::getGroup(const QString &groupName) { for (QObject *oGroup: m_groups) { GroupData *group = (GroupData *) oGroup; if (group->getName() == groupName) { return group; } } return nullptr; } void MessageHandler::removeUserFromAllGroups(UserData *user) { for (QObject *oGroup: m_groups) { GroupData *group = (GroupData *) oGroup; group->removeUser(user); } } void MessageHandler::deleteUser(const QString &userName) { for (QObject *oUser: m_users) { UserData *user = (UserData *) oUser; if (user->getName() == userName) { m_users.removeAll(user); removeUserFromAllGroups(user); delete user; emit newUsers(); } } } void MessageHandler::onLoginReceived(QTcpSocket *socket, const Login &data) { qDebug() << "Login received '" << data._name << "'"; ClientData *c = getClientData(socket); for(QObject *oClient: m_clients ) { ClientData *c = (ClientData*)oClient; if(c->getUserData() && c->getUserData()->getName() == data._name) { // found a client with the same user name.(i.e someone chose the wrong login) qDebug() << "a client with the same user name already exists"; return; //todo: // return an error message to client and inform that you have chosen the wrong login } } for(QObject *oUser: m_users) { UserData *user = (UserData*)oUser; qDebug() << "recieved login " << data._name << " " << c->getSocket(); if(user->getName() == data._name) { c->setUser(user); return; } } // Should not happen when login will be done properly... todo display an error message qDebug() << "Error: login " << data._name << " received, but no user found"; } void MessageHandler::onActivityDataReceived(const ClientData &who, const ActivityRawData &act) { qDebug() << "Activity: " << act.activityName << ", date: " << act.date << ", data:" << act.data << ", user: " << act.username; UserData *u = getUser(act.username); u->addData(act); } void MessageHandler::onNewClientReceived(const ClientData &client) { qDebug() << "New client"; ClientData *c = new ClientData(client); m_clients.push_back((QObject*)c); emit newClients(); } void MessageHandler::onClientDisconnected(QTcpSocket* socket) { qDebug() << "client disconnected"; ClientData *c = getClientData(socket); c->setUser(nullptr); m_clients.removeAll(c); delete c; emit newClients(); } ClientData *MessageHandler::getClientData(QTcpSocket* socket) { for (QObject *oc: m_clients) { ClientData *c = (ClientData *) oc; if (c->getSocket() == socket) { return c; } } } diff --git a/src/server/MessageHandler.h b/src/server/MessageHandler.h index 3b4b57bf1..f86efccc6 100644 --- a/src/server/MessageHandler.h +++ b/src/server/MessageHandler.h @@ -1,116 +1,124 @@ /* GComprisServer - MessageHandler.h * * Copyright (C) 2016 Johnny Jazeix * * Authors: * Johnny Jazeix * * 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 . */ #ifndef MESSAGEHANDLER_H #define MESSAGEHANDLER_H #include "Messages.h" #include "ClientData.h" #include "GroupData.h" #include "UserData.h" #include #include /** * @class MessageHandler * @short Handles all the messages received by the socket * * JOB: * -Handle messages received by the socket * -Create,delete, Update information about: * --users * --groups * -Linking Users,Clients and groups with each other * * * @sa UserData * @sa GroupData * @sa ClientData */ class MessageHandler: public QObject { Q_OBJECT Q_PROPERTY(QList clients MEMBER m_clients NOTIFY newClients) Q_PROPERTY(QList groups MEMBER m_groups NOTIFY newGroups) Q_PROPERTY(QList users MEMBER m_users NOTIFY newUsers) private: MessageHandler(); // prohibit external creation, we are a singleton! static MessageHandler* _instance; // singleton instance public: /** * Registers MessageHandler singleton in the QML engine. */ static void init(); static QObject *systeminfoProvider(QQmlEngine *engine, QJSEngine *scriptEngine); static MessageHandler* getInstance(); Q_INVOKABLE GroupData *createGroup(const QString &groupName,const QString &description=QString(), const QStringList& users=QStringList()); Q_INVOKABLE void deleteGroup(const QString &groupName); Q_INVOKABLE UserData *createUser(const QString &userName, const QString &avatar = QString(), const QStringList &groups = QStringList()); Q_INVOKABLE UserData *updateUser(const QString &oldUser, const QString &newUser, const QString &avatar = QString(), const QStringList &groups = QStringList()); Q_INVOKABLE void deleteUser(const QString &userName); + Q_INVOKABLE void addUserToGroup(const QStringList& groups, const QStringList& users); + UserData *getUser(const QString &userName); GroupData *getGroup(const QString &groupName); + Q_INVOKABLE QList returnUserGroups(const QString& user) { + UserData* usr = getUser(user); + if(usr) { + return usr->getGroups(); + } + } Q_INVOKABLE QList returnGroupUsers(const QString& group){ GroupData* g = getGroup(group); if(g){ return g->getUsers(); } } public slots: void onActivityDataReceived(const ClientData &who, const ActivityRawData &act); void onLoginReceived(QTcpSocket* socket, const Login &data); void onNewClientReceived(const ClientData &client); void onClientDisconnected(QTcpSocket* socket); signals: void newClients(); void newGroups(); void newUsers(); private: ClientData *getClientData(QTcpSocket* socket); void removeUserFromAllGroups(UserData *user); // ClientData* QList m_clients; // GroupData* QList m_groups; // UserData* QList m_users; }; #endif diff --git a/src/server/UserData.cpp b/src/server/UserData.cpp index 06bf4ee32..b10429cb1 100644 --- a/src/server/UserData.cpp +++ b/src/server/UserData.cpp @@ -1,72 +1,82 @@ /* GCompris - UserData.cpp * * Copyright (C) 2016 Johnny Jazeix * * Authors: * Johnny Jazeix * * 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 . */ #include #include "Messages.h" #include "UserData.h" +#include "GroupData.h" UserData::UserData() : m_name(""), m_avatar("") { + } UserData::UserData(const UserData &user) { m_name = user.m_name; m_avatar = user.m_avatar; } UserData::~UserData() { } void UserData::setName(const QString &name) { m_name = name; emit newName(); } +QList UserData::getGroups() +{ + return m_groups; +} void UserData::setAvatar(const QString &avatar) { m_avatar = avatar; emit newAvatar(); } - +void UserData::addGroup(GroupData *group) +{ + if(!m_groups.contains((QObject*)group)) + m_groups << (QObject*)group; +} const QString &UserData::getName() const { return m_name; } void UserData::addData(const ActivityRawData &rawData) { ActivityData &act = m_activityData[rawData.activityName]; act.push_back(rawData); ActivityData *act2 = m_variantData[rawData.activityName].value(); if(!act2) act2 = new ActivityData; act2->push_back(rawData); m_variantData[rawData.activityName] = QVariant::fromValue(act2); emit newActivityData(); } const QList UserData::getActivityData(const QString &activity) { return m_activityData[activity].m_qmlData; } diff --git a/src/server/UserData.h b/src/server/UserData.h index a4d1e7941..e7126e46e 100644 --- a/src/server/UserData.h +++ b/src/server/UserData.h @@ -1,72 +1,76 @@ /* GCompris - UserData.h * * Copyright (C) 2016 Johnny Jazeix * * Authors: * Johnny Jazeix * * 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 . */ #ifndef USERDATA_H #define USERDATA_H #include #include #include "ActivityData.h" +class GroupData; +struct ActivityRawData; /** * @class UserData * @short Contains all the data relative to a user * * A user has a name and a map of all its results per activity * */ class UserData : public QObject { Q_OBJECT Q_PROPERTY(QString avatar MEMBER m_avatar NOTIFY newAvatar) Q_PROPERTY(QString name MEMBER m_name NOTIFY newName) Q_PROPERTY(QVariantMap activityData MEMBER m_variantData NOTIFY newActivityData) public: UserData(); UserData(const UserData &user); ~UserData(); void setName(const QString &name); void setAvatar(const QString &avatar); - const QString &getName() const; - + void addGroup(GroupData* group); void addData(const ActivityRawData &rawData); + QList getGroups(); + const QString &getName() const; Q_INVOKABLE const QList getActivityData(const QString &activity); private: + QList m_groups; QString m_avatar; QString m_name; QMap m_activityData; QVariantMap m_variantData; signals: void newAvatar(); void newName(); void newActivityData(); }; Q_DECLARE_METATYPE(UserData) #endif diff --git a/src/server/views/Users.qml b/src/server/views/Users.qml index 04783e41b..32c0212af 100644 --- a/src/server/views/Users.qml +++ b/src/server/views/Users.qml @@ -1,233 +1,482 @@ /* GCompris - Users.qml * * Copyright (C) 2016 Johnny Jazeix * * Authors: * Johnny Jazeix * * 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 . */ import QtQuick 2.1 import GCompris 1.0 import QtQuick.Controls 1.0 +import QtQuick.Controls.Styles 1.0 import "../../core" ActivityBase { id: activity activityInfo: QtObject { property bool demo: false } pageComponent: Item { + id: mainItem anchors.fill: parent + property string userName: userText.text + property var groups: [] + property string avatar: avatarText.text + property string mode: "" + GridView { id: users - width: activity.width - height: activity.height - cellWidth: 210 + width: parent.width + height: parent.height - (bar.height * 2) + cellWidth: parent.width/10 cellHeight: cellWidth + property string currentUser: "" model: MessageHandler.users - highlight: Rectangle { color: "lightsteelblue"; radius: 5 } + + delegate: Rectangle { - id: itemDelegate - width: 200 - height: 200 + id: delegate + width: users.cellWidth/1.25 + height: width color: "red" - property string name: modelData.name GCText { text: modelData.name } - MouseArea { - id: mouse anchors.fill: parent - onClicked: { users.currentIndex = index ; print(modelData.name) } // todo what do we do? display list of action? (update user list, send configuration?) + onClicked: { + users.currentUser = modelData.name + userConfig.visible = true + configView.visible = true + cancelButton.visible = true + + } } + } + } + Grid { - rows: 2 + rows: 1 anchors.bottom: bar.top Button { id: createUserButton - text: qsTr("Create an user") - style: GCButtonStyle {} + text: qsTr("Create a new user") + style: GCButtonStyle{} onClicked: { - createUserName.mode = "create"; - createUserName.visible = true; - createUserName.defaultText = ""; - createUserName.start(); + createUserItem.visible = true } } + } - Button { - id: updateUserButton - text: qsTr("Update selected user") - style: GCButtonStyle {} - onClicked: { - if(users.currentItem) { - createUserName.mode = "update"; - createUserName.visible = true; - createUserName.defaultText = users.currentItem.name; - createUserName.start(); + Item { + id: userConfig + visible: false + width: parent.width/2.5 + height: parent.height/1.75 + anchors.centerIn: parent + Rectangle { + id: baseRect + anchors.fill: parent + color: "grey" + opacity: 0.6 + } + + + ListView { + id: configView + anchors.fill: parent + visible: true + property var options : [ + { + "shortName": "addGroups", + "text": qsTr("add %1 to groups").arg(users.currentUser) + }, + { + "shortName": "showGroups", + "text": qsTr("show groups belonging to %1").arg(users.currentUser) + }, + { + "shortName" : "deleteGroups", + "text" : qsTr("remove %1 from the groups").arg(users.currentUser) + }, + { + "shortName" : "deleteUser", + "text": qsTr("delete this User ") + }, + { + "shortName" : "activityData", + "text": qsTr("see the data of different activities of user %1").arg(users.currentUser) + } + + ] + model: options + spacing: 3 + delegate: Button { + id: actions + Text { + text: modelData.text + anchors.centerIn: parent + color: "black" + font.pixelSize: 22 } + width: parent.width + height: 70 + style: GCButtonStyle{} + MouseArea { + anchors.fill: parent + onClicked: { + cancelButton.visible = false + configView.visible = false + if(modelData.shortName === "addGroups" ) + { + groupsView.visible = true + groupsView.model = MessageHandler.groups + addNewGroupsButton.visible = true + goBackButton.visible = true + mainItem.mode = "addGroups"; + + } + if(modelData.shortName === "showGroups") + { + groupsView.model = MessageHandler.returnUserGroups(users.currentUser) + groupsView.visible = true + goBackButton.visible = true + + } + + } + } + } - enabled: users.currentItem && users.currentIndex != -1 } + + ListView { + id: groupsView + anchors.fill: parent + visible: false + model: undefined + spacing: 50 + delegate: checkBoxDelegate + + } Button { - id: sendConfiguration - text: qsTr("Delete selected user") - style: GCButtonStyle {} - onClicked: { - // Ask confirmation first - if(users.currentItem) - MessageHandler.deleteUser(users.currentItem.name); + id: addNewGroupsButton + width: parent.width/2 + visible: false + height: parent.height/6 + style: GCButtonStyle{} + anchors.bottom: parent.bottom + text: qsTr("Add") + MouseArea { + anchors.fill: parent + onClicked: { + + addNewGroupsButton.visible = false + goBackButton.visible = false + groupsView.visible = false + userConfig.visible = false + confirmationBox.visible = true + + + + } } - enabled: users.currentItem && users.currentIndex != -1 } - Button { - id: showResults - text: qsTr("Display data") - style: GCButtonStyle {} - onClicked: { - // Ask confirmation first + id: goBackButton + width: addNewGroupsButton.visible ? parent.width/2: parent.width + visible: false + height: parent.height/6 + style: GCButtonStyle{} + anchors.bottom: parent.bottom + anchors.right: parent.right + text: qsTr("Back") + MouseArea { + anchors.fill: parent + onClicked:{ + addNewGroupsButton.visible = false + goBackButton.visible = false + groupsView.visible = false + configView.visible = true + cancelButton.visible = true + } } - enabled: users.currentItem && users.currentIndex != -1 } + } - GCInputDialog { - id: createUserName + + Item { + id: createUserItem visible: false - active: visible - anchors.fill: parent - z: 100 - property string mode: "create" - - message: mode == "create" ? qsTr("Name of the new user") : qsTr("Update user %1").arg(users.currentItem.name) - onClose: createUserName.visible = false; - - button1Text: qsTr("OK") - button2Text: qsTr("Cancel") - onButton1Hit: { - if(MessageHandler.groups.length !== 0) { - chooseLogin.visible = true; - chooseLogin.username = createUserName.inputtedText - chooseLogin.start(); - } - else { - // no users, create the group directly - if(mode == "create") { - MessageHandler.createUser(createUserName.inputtedText, "") + width:parent.width/1.75 + height: parent.height/1.5 + anchors.centerIn: parent + Rectangle { + id: createUserRect + anchors.fill: parent + color: "grey" + opacity: 0.6 + Button { + id: userName + anchors.left: parent.left + height: parent.height/8 + width: parent.width/4 + + text: qsTr("User Name") + + style: GCButtonStyle{} + } + TextField { + id: userText + anchors.left: userName.right + anchors.leftMargin: 30 + anchors.right: parent.right + height: userName.height + text: "" + placeholderText: qsTr("Enter the name of the user") + } + + Button { + id: avatar + anchors.left: parent.left + anchors.top: userName.bottom + anchors.topMargin: 50 + height: parent.height/8 + width: parent.width/4 + text: qsTr("Avatar") + style: GCButtonStyle{} + } + TextField { + id: avatarText + anchors.left: avatar.right + anchors.leftMargin: 30 + anchors.top: userText.bottom + anchors.topMargin: 50 + anchors.right: parent.right + height: avatar.height + text: "" + placeholderText: qsTr("Add avatar for the user") + } + Button { + id: addGroups + height: parent.height/8 + anchors.left: parent.left + anchors.topMargin: 50 + anchors.top: avatar.bottom + width: parent.width/4 + text: qsTr("Add Groups") + style: GCButtonStyle{} + } + + ListView { + id: chooseGroups + anchors.left: addGroups.right + anchors.right: parent.right + anchors.top: avatarText.bottom + anchors.topMargin: 50 + anchors.leftMargin: 30 + height: parent.height/4 + model: MessageHandler.groups + spacing: 50 + Component { + id: checkBoxDelegate + GCDialogCheckBox { + id: checkbox + width: parent.width + text:modelData.name + onCheckedChanged: { + if(checkbox.checked) { + console.log("checked ",modelData.name) + mainItem.groups.push(modelData.name) + } + + else { + mainItem.groups.pop(modelData.name) + console.log("unchecked ", modelData.name) + } + } + + style: CheckBoxStyle { + spacing: 10 + + indicator: Image { + sourceSize.height: 50 + property string suffix: control.enabled ? ".svg" : "_disabled.svg" + source: + control.checked ? "qrc:/gcompris/src/core/resource/apply" + suffix : + "qrc:/gcompris/src/core/resource/cancel" + suffix + } + label: GCText { + fontSize: mediumSize + text: control.text + wrapMode: Text.WordWrap + width: parent.parent.width - 50 * ApplicationInfo.ratio - 10 * 2 + } + + + } + } + } + + delegate: checkBoxDelegate + + } + Button { + id: create + width: parent.width/2 + anchors.bottom: parent.bottom + height: parent.height/6 + style: GCButtonStyle{} + text: qsTr("Create") + MouseArea { + anchors.fill: parent + onClicked: { + // display the confirmation box + mainItem.mode = "create" + confirmationBox.visible = true + createUserItem.visible = false + + } } - else { - MessageHandler.updateUser(users.currentItem.name, createUserName.inputtedText, "") + + } + Button { + id: cancel + anchors.left: create.right + anchors.right: parent.right + anchors.bottom: parent.bottom + height: parent.height/6 + style: GCButtonStyle{} + text: qsTr("Cancel") + MouseArea { + anchors.fill: parent + onClicked: { + createUserItem.visible = false + + } } + } - } - focus: true - onStart: { inputItem.text = defaultText; inputItem.forceActiveFocus() } - onStop: activity.forceActiveFocus() - - /** - * type:string - * inputted default text in the TextInput. - */ - property string defaultText - - /** - * type:string - * inputted text in the TextInput. - */ - property string inputtedText: inputItem ? inputItem.text : "" - - content: TextInput { - id: textInput - height: 60 * ApplicationInfo.ratio - horizontalAlignment: TextInput.AlignHCenter - verticalAlignment: TextInput.AlignVCenter - text: createUserName.defaultText - font.pointSize: 14 - font.weight: Font.DemiBold } } - - GCInputDialog { - id: chooseLogin + Rectangle { + id: confirmationBox + width: parent.width/2 + height: parent.height/4.5 + anchors.centerIn: parent + color: "grey" + opacity: 0.6 visible: false - active: visible - anchors.fill: parent - - message: qsTr("Add user to existing group") - onClose: chooseLogin.visible = false; + GCText { + text: { + if (mainItem.mode === "create") + return "Are you sure you want to create this user?" + else if(mainItem.mode === "addGroups") + return "Are you sure you want to add " + users.currentUser + " to the slected groups ?" + else + return "" + } - property string username + wrapMode: Text.Wrap + font.pixelSize: 25 + anchors.centerIn: parent - button1Text: qsTr("OK") - onButton1Hit: { - createUserName.mode == "create" ? - MessageHandler.createUser(username, "", selectedGroups) : - MessageHandler.updateUser(users.currentItem.name, username, "", selectedGroups) - chooseLogin.selectedGroups = []; } - focus: true - - property string chosenLogin - property var model: MessageHandler.groups - - property var selectedGroups: [] - content: ListView { - id: view - width: chooseLogin.width - height: 100 * ApplicationInfo.ratio - contentHeight: 60 * ApplicationInfo.ratio * model.count - interactive: true - clip: true - model: chooseLogin.model - delegate: GCDialogCheckBox { - id: userBox - text: modelData.name - // if you create a user, it's not in any group - // (need to handle case of existing name) - checked: createUserName.mode == "create" ? false : - modelData.hasUser(users.currentItem.name) - onCheckedChanged: { - if(checked) { - chooseLogin.selectedGroups.push(modelData.name) - print("checked " + modelData.name) - print("checked " + chooseLogin.selectedGroups) + Button { + id: yes + anchors.bottom: parent.bottom + height: parent.height/6 + width: parent.width/2 + style: GCButtonStyle{} + text: qsTr("YES") + MouseArea { + anchors.fill: parent + onClicked: { + + if (mainItem.mode === "create") + { + console.log(mainItem.userName," ",mainItem.avatar, " ", + mainItem.groups) + MessageHandler.createUser(mainItem.userName, mainItem.avatar, mainItem.groups) + } - else { - chooseLogin.selectedGroups.splice(chooseLogin.selectedGroups.indexOf(modelData.name), 1) - print("not checked" + modelData.name) + if(mainItem.mode === "addGroups") + { + console.log(users.currentUser, mainItem.groups) + MessageHandler.addUserToGroup(mainItem.groups, users.currentUser) } + + userText.text = "" + avatarText.text = "" + mainItem.groups = [] + confirmationBox.visible = false + } } } - } + Button { + id: no + anchors.bottom: parent.bottom + height: parent.height/6 + anchors.left: yes.right + width: parent.width/2 + style: GCButtonStyle{} + text: qsTr("NO") + MouseArea { + anchors.fill: parent + onClicked: { + // set all the values to "" + userText.text = "" + avatarText.text = "" + mainItem.groups = [] + confirmationBox.visible = false + } + } + } + } + GCButtonCancel { + id: cancelButton + visible: false + anchors.right: undefined + anchors.top: undefined + anchors.bottom: userConfig.top + anchors.left: userConfig.right + anchors.margins: 0 + onClose: { + cancelButton.visible = false + userConfig.visible = false + } + } Bar { + id: bar content: BarEnumContent { value: home } onHomeClicked: activity.home() } - } + + } }