diff --git a/cockatrice/src/abstractclient.cpp b/cockatrice/src/abstractclient.cpp index f2e8915b..6faf6b33 100644 --- a/cockatrice/src/abstractclient.cpp +++ b/cockatrice/src/abstractclient.cpp @@ -8,6 +8,7 @@ #include "pb/event_server_shutdown.pb.h" #include "pb/event_connection_closed.pb.h" #include "pb/event_user_message.pb.h" +#include "pb/event_notify_user.pb.h" #include "pb/event_list_rooms.pb.h" #include "pb/event_add_to_list.pb.h" #include "pb/event_remove_from_list.pb.h" @@ -40,6 +41,7 @@ AbstractClient::AbstractClient(QObject *parent) qRegisterMetaType("Event_ListRooms"); qRegisterMetaType("Event_GameJoined"); qRegisterMetaType("Event_UserMessage"); + qRegisterMetaType("Event_NotifyUser"); qRegisterMetaType("ServerInfo_User"); qRegisterMetaType >("QList"); qRegisterMetaType("Event_ReplayAdded"); @@ -75,6 +77,7 @@ void AbstractClient::processProtocolItem(const ServerMessage &item) case SessionEvent::SERVER_SHUTDOWN: emit serverShutdownEventReceived(event.GetExtension(Event_ServerShutdown::ext)); break; case SessionEvent::CONNECTION_CLOSED: emit connectionClosedEventReceived(event.GetExtension(Event_ConnectionClosed::ext)); break; case SessionEvent::USER_MESSAGE: emit userMessageEventReceived(event.GetExtension(Event_UserMessage::ext)); break; + case SessionEvent::NOTIFY_USER: emit notifyUserEventReceived(event.GetExtension(Event_NotifyUser::ext)); break; case SessionEvent::LIST_ROOMS: emit listRoomsEventReceived(event.GetExtension(Event_ListRooms::ext)); break; case SessionEvent::ADD_TO_LIST: emit addToListEventReceived(event.GetExtension(Event_AddToList::ext)); break; case SessionEvent::REMOVE_FROM_LIST: emit removeFromListEventReceived(event.GetExtension(Event_RemoveFromList::ext)); break; diff --git a/cockatrice/src/abstractclient.h b/cockatrice/src/abstractclient.h index df029a1e..939066bc 100644 --- a/cockatrice/src/abstractclient.h +++ b/cockatrice/src/abstractclient.h @@ -21,6 +21,7 @@ class Event_ServerMessage; class Event_ListRooms; class Event_GameJoined; class Event_UserMessage; +class Event_NotifyUser; class Event_ConnectionClosed; class Event_ServerShutdown; class Event_ReplayAdded; @@ -56,6 +57,7 @@ signals: void listRoomsEventReceived(const Event_ListRooms &event); void gameJoinedEventReceived(const Event_GameJoined &event); void userMessageEventReceived(const Event_UserMessage &event); + void notifyUserEventReceived(const Event_NotifyUser &event); void userInfoChanged(const ServerInfo_User &userInfo); void buddyListReceived(const QList &buddyList); void ignoreListReceived(const QList &ignoreList); diff --git a/cockatrice/src/tab_supervisor.cpp b/cockatrice/src/tab_supervisor.cpp index 47da8702..9bde53b2 100644 --- a/cockatrice/src/tab_supervisor.cpp +++ b/cockatrice/src/tab_supervisor.cpp @@ -1,3 +1,6 @@ +#include +#include +#include #include #include "tab_supervisor.h" #include "abstractclient.h" @@ -13,14 +16,12 @@ #include "pixmapgenerator.h" #include "userlist.h" #include "settingscache.h" -#include -#include -#include #include "pb/room_commands.pb.h" #include "pb/room_event.pb.h" #include "pb/game_event_container.pb.h" #include "pb/event_user_message.pb.h" +#include "pb/event_notify_user.pb.h" #include "pb/event_game_joined.pb.h" #include "pb/serverinfo_user.pb.h" #include "pb/serverinfo_room.pb.h" @@ -90,6 +91,7 @@ TabSupervisor::TabSupervisor(AbstractClient *_client, QWidget *parent) connect(client, SIGNAL(gameJoinedEventReceived(const Event_GameJoined &)), this, SLOT(gameJoined(const Event_GameJoined &))); connect(client, SIGNAL(userMessageEventReceived(const Event_UserMessage &)), this, SLOT(processUserMessageEvent(const Event_UserMessage &))); connect(client, SIGNAL(maxPingTime(int, int)), this, SLOT(updatePingTime(int, int))); + connect(client, SIGNAL(notifyUserEventReceived(const Event_NotifyUser &)), this, SLOT(processNotifyUserEvent(const Event_NotifyUser &))); retranslateUi(); } @@ -559,3 +561,13 @@ bool TabSupervisor::getAdminLocked() const return true; return tabAdmin->getLocked(); } + +void TabSupervisor::processNotifyUserEvent(const Event_NotifyUser &event) +{ + switch ((Event_NotifyUser::NotificationType) event.type()) { + case Event_NotifyUser::PROMOTED: QMessageBox::information(this, tr("Promotion"), tr("You have been promoted to moderator. Please log out and back in for changes to take effect.")); break; + default: ; + } + +} + diff --git a/cockatrice/src/tab_supervisor.h b/cockatrice/src/tab_supervisor.h index a29c316c..13722032 100644 --- a/cockatrice/src/tab_supervisor.h +++ b/cockatrice/src/tab_supervisor.h @@ -22,6 +22,7 @@ class RoomEvent; class GameEventContainer; class Event_GameJoined; class Event_UserMessage; +class Event_NotifyUser; class ServerInfo_Room; class ServerInfo_User; class GameReplay; @@ -105,6 +106,7 @@ private slots: void processRoomEvent(const RoomEvent &event); void processGameEventContainer(const GameEventContainer &cont); void processUserMessageEvent(const Event_UserMessage &event); + void processNotifyUserEvent(const Event_NotifyUser &event); }; -#endif +#endif \ No newline at end of file diff --git a/cockatrice/src/user_context_menu.cpp b/cockatrice/src/user_context_menu.cpp index 758f99e1..2925cd19 100644 --- a/cockatrice/src/user_context_menu.cpp +++ b/cockatrice/src/user_context_menu.cpp @@ -1,5 +1,6 @@ #include #include +#include #include "user_context_menu.h" #include "tab_supervisor.h" #include "tab_userlists.h" @@ -31,6 +32,8 @@ UserContextMenu::UserContextMenu(const TabSupervisor *_tabSupervisor, QWidget *p aRemoveFromIgnoreList = new QAction(QString(), this); aKick = new QAction(QString(), this); aBan = new QAction(QString(), this); + aPromoteToMod = new QAction(QString(), this); + aDemoteFromMod = new QAction(QString(), this); retranslateUi(); } @@ -46,6 +49,8 @@ void UserContextMenu::retranslateUi() aRemoveFromIgnoreList->setText(tr("Remove from &ignore list")); aKick->setText(tr("Kick from &game")); aBan->setText(tr("Ban from &server")); + aPromoteToMod->setText(tr("&Promote user to moderator")); + aDemoteFromMod->setText(tr("Dem&ote user from moderator")); } void UserContextMenu::gamesOfUserReceived(const Response &resp, const CommandContainer &commandContainer) @@ -89,6 +94,27 @@ void UserContextMenu::banUser_processUserInfoResponse(const Response &r) dlg->show(); } +void UserContextMenu::adjustMod_processUserResponse(const Response &resp, const CommandContainer &commandContainer) +{ + + const Command_AdjustMod &cmd = commandContainer.admin_command(0).GetExtension(Command_AdjustMod::ext); + + if (resp.response_code() == Response::RespOk) { + if (cmd.should_be_mod()) { + QMessageBox::information(static_cast(parent()), tr("Success"), tr("Successfully promoted user.")); + } else { + QMessageBox::information(static_cast(parent()), tr("Success"), tr("Successfully demoted user.")); + } + + } else { + if (cmd.should_be_mod()) { + QMessageBox::information(static_cast(parent()), tr("Failed"), tr("Failed to promote user.")); + } else { + QMessageBox::information(static_cast(parent()), tr("Failed"), tr("Failed to demote user.")); + } + } +} + void UserContextMenu::banUser_dialogFinished() { BanDialog *dlg = static_cast(sender()); @@ -132,6 +158,14 @@ void UserContextMenu::showContextMenu(const QPoint &pos, const QString &userName if (!tabSupervisor->getAdminLocked()) { menu->addSeparator(); menu->addAction(aBan); + + menu->addSeparator(); + if (userLevel.testFlag(ServerInfo_User::IsModerator) && (tabSupervisor->getUserInfo()->user_level() & ServerInfo_User::IsAdmin)) { + menu->addAction(aDemoteFromMod); + + } else if (userLevel.testFlag(ServerInfo_User::IsRegistered) && (tabSupervisor->getUserInfo()->user_level() & ServerInfo_User::IsAdmin)) { + menu->addAction(aPromoteToMod); + } } bool anotherUser = userName != QString::fromStdString(tabSupervisor->getUserInfo()->name()); aDetails->setEnabled(online); @@ -143,6 +177,8 @@ void UserContextMenu::showContextMenu(const QPoint &pos, const QString &userName aRemoveFromIgnoreList->setEnabled(anotherUser); aKick->setEnabled(anotherUser); aBan->setEnabled(anotherUser); + aPromoteToMod->setEnabled(anotherUser); + aDemoteFromMod->setEnabled(anotherUser); QAction *actionClicked = menu->exec(pos); if (actionClicked == aDetails) { @@ -186,6 +222,7 @@ void UserContextMenu::showContextMenu(const QPoint &pos, const QString &userName } else if (actionClicked == aKick) { Command_KickFromGame cmd; cmd.set_player_id(playerId); + game->sendGameCommand(cmd); } else if (actionClicked == aBan) { Command_GetUserInfo cmd; @@ -193,7 +230,24 @@ void UserContextMenu::showContextMenu(const QPoint &pos, const QString &userName PendingCommand *pend = client->prepareSessionCommand(cmd); connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this, SLOT(banUser_processUserInfoResponse(Response))); + client->sendCommand(pend); + } else if (actionClicked == aPromoteToMod) { + Command_AdjustMod cmd; + cmd.set_user_name(userName.toStdString()); + cmd.set_should_be_mod(true); + // client->sendCommand(client->prepareAdminCommand(cmd)); + PendingCommand *pend = client->prepareAdminCommand(cmd); + connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this, SLOT(adjustMod_processUserResponse(Response,CommandContainer))); + client->sendCommand(pend); + } else if (actionClicked == aDemoteFromMod) { + Command_AdjustMod cmd; + cmd.set_user_name(userName.toStdString()); + cmd.set_should_be_mod(false); + + // client->sendCommand(client->prepareAdminCommand(cmd)); + PendingCommand *pend = client->prepareAdminCommand(cmd); + connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this, SLOT(adjustMod_processUserResponse(Response,CommandContainer))); client->sendCommand(pend); } diff --git a/cockatrice/src/user_context_menu.h b/cockatrice/src/user_context_menu.h index 9aeadfd4..dd0d1ed5 100644 --- a/cockatrice/src/user_context_menu.h +++ b/cockatrice/src/user_context_menu.h @@ -27,10 +27,12 @@ private: QAction *aAddToIgnoreList, *aRemoveFromIgnoreList; QAction *aKick; QAction *aBan; + QAction *aPromoteToMod, *aDemoteFromMod; signals: void openMessageDialog(const QString &userName, bool focus); private slots: void banUser_processUserInfoResponse(const Response &resp); + void adjustMod_processUserResponse(const Response &resp, const CommandContainer &commandContainer); void banUser_dialogFinished(); void gamesOfUserReceived(const Response &resp, const CommandContainer &commandContainer); public: diff --git a/common/pb/CMakeLists.txt b/common/pb/CMakeLists.txt index db5cd130..d58846c6 100644 --- a/common/pb/CMakeLists.txt +++ b/common/pb/CMakeLists.txt @@ -106,6 +106,7 @@ SET(PROTO_FILES event_user_joined.proto event_user_left.proto event_user_message.proto + event_notify_user.proto game_commands.proto game_event_container.proto game_event_context.proto @@ -127,6 +128,7 @@ SET(PROTO_FILES response_register.proto response_replay_download.proto response_replay_list.proto + response_adjust_mod.proto response.proto room_commands.proto room_event.proto diff --git a/common/pb/admin_commands.proto b/common/pb/admin_commands.proto index b23f745d..8e2757ae 100644 --- a/common/pb/admin_commands.proto +++ b/common/pb/admin_commands.proto @@ -3,6 +3,7 @@ message AdminCommand { UPDATE_SERVER_MESSAGE = 1000; SHUTDOWN_SERVER = 1001; RELOAD_CONFIG = 1002; + ADJUST_MOD = 1003; } extensions 100 to max; } @@ -26,3 +27,12 @@ message Command_ReloadConfig { optional Command_ReloadConfig ext = 1002; } } + +message Command_AdjustMod { + extend AdminCommand { + optional Command_AdjustMod ext = 1003; + } + required string user_name = 1; + required bool should_be_mod = 2; +} + diff --git a/common/pb/event_connection_closed.proto b/common/pb/event_connection_closed.proto index d5972aa4..c59a4f74 100644 --- a/common/pb/event_connection_closed.proto +++ b/common/pb/event_connection_closed.proto @@ -11,6 +11,7 @@ message Event_ConnectionClosed { BANNED = 4; USERNAMEINVALID = 5; USER_LIMIT_REACHED = 6; + DEMOTED = 7; } optional CloseReason reason = 1; optional string reason_str = 2; diff --git a/common/pb/event_notify_user.proto b/common/pb/event_notify_user.proto new file mode 100644 index 00000000..e300f45a --- /dev/null +++ b/common/pb/event_notify_user.proto @@ -0,0 +1,14 @@ +import "session_event.proto"; + +message Event_NotifyUser { + + enum NotificationType { + PROMOTED = 1; + } + + extend SessionEvent { + optional Event_NotifyUser ext = 1010; + } + optional NotificationType type = 1; + +} diff --git a/common/pb/response.proto b/common/pb/response.proto index ae4ff040..aa004006 100644 --- a/common/pb/response.proto +++ b/common/pb/response.proto @@ -49,6 +49,7 @@ message Response { DECK_UPLOAD = 1008; REGISTER = 1009; ACTIVATE = 1010; + ADJUST_MOD = 1011; REPLAY_LIST = 1100; REPLAY_DOWNLOAD = 1101; } diff --git a/common/pb/response_adjust_mod.proto b/common/pb/response_adjust_mod.proto new file mode 100644 index 00000000..e5dac6d8 --- /dev/null +++ b/common/pb/response_adjust_mod.proto @@ -0,0 +1,7 @@ +import "response.proto"; + +message Response_AdjustMod{ + extend Response { + optional Response_AdjustMod ext = 1011; + } +} diff --git a/common/pb/session_event.proto b/common/pb/session_event.proto index a7b14d00..1bfcbf88 100644 --- a/common/pb/session_event.proto +++ b/common/pb/session_event.proto @@ -12,6 +12,7 @@ message SessionEvent { USER_JOINED = 1007; USER_LEFT = 1008; GAME_JOINED = 1009; + NOTIFY_USER = 1010; REPLAY_ADDED = 1100; } extensions 100 to max; diff --git a/servatrice/src/serversocketinterface.cpp b/servatrice/src/serversocketinterface.cpp index db87c70d..a50ca128 100644 --- a/servatrice/src/serversocketinterface.cpp +++ b/servatrice/src/serversocketinterface.cpp @@ -49,6 +49,7 @@ #include "pb/event_server_identification.pb.h" #include "pb/event_add_to_list.pb.h" #include "pb/event_remove_from_list.pb.h" +#include "pb/event_notify_user.pb.h" #include "pb/response_deck_list.pb.h" #include "pb/response_deck_download.pb.h" #include "pb/response_deck_upload.pb.h" @@ -309,6 +310,7 @@ Response::ResponseCode ServerSocketInterface::processExtendedAdminCommand(int cm case AdminCommand::SHUTDOWN_SERVER: return cmdShutdownServer(cmd.GetExtension(Command_ShutdownServer::ext), rc); case AdminCommand::UPDATE_SERVER_MESSAGE: return cmdUpdateServerMessage(cmd.GetExtension(Command_UpdateServerMessage::ext), rc); case AdminCommand::RELOAD_CONFIG: return cmdReloadConfig(cmd.GetExtension(Command_ReloadConfig::ext), rc); + case AdminCommand::ADJUST_MOD: return cmdAdjustMod(cmd.GetExtension(Command_AdjustMod::ext), rc); default: return Response::RespFunctionNotAllowed; } } @@ -1000,3 +1002,54 @@ Response::ResponseCode ServerSocketInterface::cmdReloadConfig(const Command_Relo settingsCache->sync(); return Response::RespOk; } + +Response::ResponseCode ServerSocketInterface::cmdAdjustMod(const Command_AdjustMod &cmd, ResponseContainer & /*rc*/) { + + QString userName = QString::fromStdString(cmd.user_name()); + + if (cmd.should_be_mod()) { + logDebugMessage("Received admin command: promote user"); + QSqlQuery *query = sqlInterface->prepareQuery( + "update {prefix}_users set admin = :adminlevel where name = :username"); + query->bindValue(":adminlevel", 2); + query->bindValue(":username", userName); + if (!sqlInterface->execSqlQuery(query)) + return Response::RespInternalError; + + ServerSocketInterface *user = static_cast(server->getUsers().value(userName)); + Event_NotifyUser event; + event.set_type(Event_NotifyUser::PROMOTED); + SessionEvent *se = user->prepareSessionEvent(event); + user->sendProtocolItem(*se); + delete se; + + } else { + logDebugMessage("Received admin command: demote user"); + QSqlQuery *query = sqlInterface->prepareQuery("update {prefix}_users set admin = :adminlevel where name = :username"); + query->bindValue(":adminlevel", 0); + query->bindValue(":username", userName); + if (!sqlInterface->execSqlQuery(query)) + return Response::RespInternalError; + + QList userList; + ServerSocketInterface *user = static_cast(server->getUsers().value(userName)); + userList.append(user); + + if (!userList.isEmpty()) { + Event_ConnectionClosed event; + event.set_reason(Event_ConnectionClosed::DEMOTED); + event.set_reason_str("Your moderator status has been revoked."); + event.set_end_time(QDateTime::currentDateTime().toTime_t()); + for (int i = 0; i < userList.size(); ++i) { + SessionEvent *se = userList[i]->prepareSessionEvent(event); + userList[i]->sendProtocolItem(*se); + delete se; + QMetaObject::invokeMethod(userList[i], "prepareDestroy", Qt::QueuedConnection); + } + userList.clear(); + } + + } + + return Response::RespOk; +} \ No newline at end of file diff --git a/servatrice/src/serversocketinterface.h b/servatrice/src/serversocketinterface.h index ccec5519..a733c6b7 100644 --- a/servatrice/src/serversocketinterface.h +++ b/servatrice/src/serversocketinterface.h @@ -100,6 +100,7 @@ private: Response::ResponseCode cmdRegisterAccount(const Command_Register &cmd, ResponseContainer &rc); Response::ResponseCode cmdActivateAccount(const Command_Activate &cmd, ResponseContainer & /* rc */); Response::ResponseCode cmdReloadConfig(const Command_ReloadConfig &/* cmd */, ResponseContainer & /*rc*/); + Response::ResponseCode cmdAdjustMod(const Command_AdjustMod &cmd, ResponseContainer & /*rc*/); Response::ResponseCode processExtendedSessionCommand(int cmdType, const SessionCommand &cmd, ResponseContainer &rc); Response::ResponseCode processExtendedModeratorCommand(int cmdType, const ModeratorCommand &cmd, ResponseContainer &rc);