mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-09-28 00:42:19 -07:00
[Client] Add moderation tab with investigate, password reset, and avatar removal
- New Moderation tab: search a user to show account info, alternate accounts, login sessions, and staff last logins; actions to reset the user's password (shows the generated temporary password) and remove the user's avatar - 'Investigate user' entry in the user context menu opens the tab pre-loaded for that user - Warning dialog shows the infraction level of each warning category - Tab wired into TabSupervisor with a moderator-gated menu action, shortcut, and tabs.ini persistence (default closed)
This commit is contained in:
parent
ae58cdd2de
commit
c6d8bce81f
14 changed files with 640 additions and 6 deletions
|
|
@ -379,6 +379,7 @@ set(cockatrice_SOURCES
|
||||||
src/interface/widgets/tabs/tab_home.cpp
|
src/interface/widgets/tabs/tab_home.cpp
|
||||||
src/interface/widgets/tabs/tab_logs.cpp
|
src/interface/widgets/tabs/tab_logs.cpp
|
||||||
src/interface/widgets/tabs/tab_message.cpp
|
src/interface/widgets/tabs/tab_message.cpp
|
||||||
|
src/interface/widgets/tabs/tab_moderation.cpp
|
||||||
src/interface/widgets/tabs/tab_report.cpp
|
src/interface/widgets/tabs/tab_report.cpp
|
||||||
src/interface/widgets/tabs/tab_replays.cpp
|
src/interface/widgets/tabs/tab_replays.cpp
|
||||||
src/interface/widgets/tabs/tab_room.cpp
|
src/interface/widgets/tabs/tab_room.cpp
|
||||||
|
|
|
||||||
|
|
@ -788,6 +788,8 @@ private:
|
||||||
ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Logs"), parseSequenceString(""), ShortcutGroup::Tabs)},
|
ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Logs"), parseSequenceString(""), ShortcutGroup::Tabs)},
|
||||||
{"Tabs/aTabReport",
|
{"Tabs/aTabReport",
|
||||||
ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Report Queue"), parseSequenceString(""), ShortcutGroup::Tabs)},
|
ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Report Queue"), parseSequenceString(""), ShortcutGroup::Tabs)},
|
||||||
|
{"Tabs/aTabModeration",
|
||||||
|
ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Moderation"), parseSequenceString(""), ShortcutGroup::Tabs)},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,7 @@ UserContextMenu::UserContextMenu(TabSupervisor *_tabSupervisor, QWidget *parent,
|
||||||
aPromoteToJudge = new QAction(QString(), this);
|
aPromoteToJudge = new QAction(QString(), this);
|
||||||
aDemoteFromJudge = new QAction(QString(), this);
|
aDemoteFromJudge = new QAction(QString(), this);
|
||||||
aGetAdminNotes = new QAction(QString(), this);
|
aGetAdminNotes = new QAction(QString(), this);
|
||||||
|
aInvestigateUser = new QAction(QString(), this);
|
||||||
|
|
||||||
retranslateUi();
|
retranslateUi();
|
||||||
}
|
}
|
||||||
|
|
@ -76,6 +77,7 @@ void UserContextMenu::retranslateUi()
|
||||||
aPromoteToJudge->setText(tr("Promote user to &judge"));
|
aPromoteToJudge->setText(tr("Promote user to &judge"));
|
||||||
aDemoteFromJudge->setText(tr("Demote user from judge"));
|
aDemoteFromJudge->setText(tr("Demote user from judge"));
|
||||||
aGetAdminNotes->setText(tr("View admin notes"));
|
aGetAdminNotes->setText(tr("View admin notes"));
|
||||||
|
aInvestigateUser->setText(tr("Investigate user"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void UserContextMenu::gamesOfUserReceived(const Response &resp, const CommandContainer &commandContainer)
|
void UserContextMenu::gamesOfUserReceived(const Response &resp, const CommandContainer &commandContainer)
|
||||||
|
|
@ -147,7 +149,8 @@ void UserContextMenu::warnUser_processGetWarningsListResponse(const Response &r)
|
||||||
|
|
||||||
if (response.warning_size() > 0) {
|
if (response.warning_size() > 0) {
|
||||||
for (int i = 0; i < response.warning_size(); ++i) {
|
for (int i = 0; i < response.warning_size(); ++i) {
|
||||||
dlg->addWarningOption(QString::fromStdString(response.warning(i)).simplified());
|
int startingIl = i < response.warning_il_size() ? response.warning_il(i) : 1;
|
||||||
|
dlg->addWarningOption(QString::fromStdString(response.warning(i)).simplified(), startingIl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dlg->show();
|
dlg->show();
|
||||||
|
|
@ -414,6 +417,7 @@ void UserContextMenu::showContextMenu(const QPoint &pos,
|
||||||
menu->addAction(aBanHistory);
|
menu->addAction(aBanHistory);
|
||||||
menu->addSeparator();
|
menu->addSeparator();
|
||||||
menu->addAction(aGetAdminNotes);
|
menu->addAction(aGetAdminNotes);
|
||||||
|
menu->addAction(aInvestigateUser);
|
||||||
|
|
||||||
menu->addSeparator();
|
menu->addSeparator();
|
||||||
if (userLevel.testFlag(ServerInfo_User::IsModerator) &&
|
if (userLevel.testFlag(ServerInfo_User::IsModerator) &&
|
||||||
|
|
@ -448,6 +452,7 @@ void UserContextMenu::showContextMenu(const QPoint &pos,
|
||||||
aBan->setEnabled(anotherUser);
|
aBan->setEnabled(anotherUser);
|
||||||
aBanHistory->setEnabled(anotherUser);
|
aBanHistory->setEnabled(anotherUser);
|
||||||
aGetAdminNotes->setEnabled(anotherUser);
|
aGetAdminNotes->setEnabled(anotherUser);
|
||||||
|
aInvestigateUser->setEnabled(anotherUser);
|
||||||
aPromoteToMod->setEnabled(anotherUser);
|
aPromoteToMod->setEnabled(anotherUser);
|
||||||
aDemoteFromMod->setEnabled(anotherUser);
|
aDemoteFromMod->setEnabled(anotherUser);
|
||||||
|
|
||||||
|
|
@ -492,6 +497,8 @@ void UserContextMenu::showContextMenu(const QPoint &pos,
|
||||||
execWarnHistory(userName);
|
execWarnHistory(userName);
|
||||||
} else if (actionClicked == aGetAdminNotes) {
|
} else if (actionClicked == aGetAdminNotes) {
|
||||||
execAdminNotes(userName);
|
execAdminNotes(userName);
|
||||||
|
} else if (actionClicked == aInvestigateUser) {
|
||||||
|
execInvestigateUser(userName);
|
||||||
} else if (actionClicked == aCopyToClipBoard) {
|
} else if (actionClicked == aCopyToClipBoard) {
|
||||||
QClipboard *clipboard = QGuiApplication::clipboard();
|
QClipboard *clipboard = QGuiApplication::clipboard();
|
||||||
clipboard->setText(deckHash);
|
clipboard->setText(deckHash);
|
||||||
|
|
@ -668,6 +675,11 @@ void UserContextMenu::execAdminNotes(const QString &userName)
|
||||||
client->sendCommand(pend);
|
client->sendCommand(pend);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void UserContextMenu::execInvestigateUser(const QString &userName)
|
||||||
|
{
|
||||||
|
tabSupervisor->openTabModeration(userName);
|
||||||
|
}
|
||||||
|
|
||||||
void UserContextMenu::execAdjustMod(const QString &userName, bool shouldBeMod)
|
void UserContextMenu::execAdjustMod(const QString &userName, bool shouldBeMod)
|
||||||
{
|
{
|
||||||
Command_AdjustMod cmd;
|
Command_AdjustMod cmd;
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@ private:
|
||||||
QAction *aWarnUser, *aWarnHistory;
|
QAction *aWarnUser, *aWarnHistory;
|
||||||
QAction *aGetAdminNotes;
|
QAction *aGetAdminNotes;
|
||||||
std::function<QList<GameInviteOption>()> gameInviteLinkProvider;
|
std::function<QList<GameInviteOption>()> gameInviteLinkProvider;
|
||||||
|
QAction *aInvestigateUser;
|
||||||
signals:
|
signals:
|
||||||
void openMessageDialog(const QString &userName, bool focus);
|
void openMessageDialog(const QString &userName, bool focus);
|
||||||
private slots:
|
private slots:
|
||||||
|
|
@ -119,6 +120,7 @@ public:
|
||||||
void execBanHistory(const QString &userName);
|
void execBanHistory(const QString &userName);
|
||||||
void execWarnHistory(const QString &userName);
|
void execWarnHistory(const QString &userName);
|
||||||
void execAdminNotes(const QString &userName);
|
void execAdminNotes(const QString &userName);
|
||||||
|
void execInvestigateUser(const QString &userName);
|
||||||
void execAdjustMod(const QString &userName, bool shouldBeMod);
|
void execAdjustMod(const QString &userName, bool shouldBeMod);
|
||||||
void execAdjustJudge(const QString &userName, bool shouldBeJudge);
|
void execAdjustJudge(const QString &userName, bool shouldBeJudge);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -213,9 +213,13 @@ int WarningDialog::getDeleteMessages() const
|
||||||
return deleteMessages->isChecked() ? -1 : 0;
|
return deleteMessages->isChecked() ? -1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WarningDialog::addWarningOption(const QString warning)
|
void WarningDialog::addWarningOption(const QString warning, int startingIl)
|
||||||
{
|
{
|
||||||
warningOption->addItem(warning);
|
if (startingIl > 1) {
|
||||||
|
warningOption->addItem(tr("%1 (IL %2)").arg(warning).arg(startingIl));
|
||||||
|
} else {
|
||||||
|
warningOption->addItem(warning);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void BanDialog::okClicked()
|
void BanDialog::okClicked()
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,7 @@ public:
|
||||||
[[nodiscard]] QString getWarnID() const;
|
[[nodiscard]] QString getWarnID() const;
|
||||||
[[nodiscard]] QString getReason() const;
|
[[nodiscard]] QString getReason() const;
|
||||||
[[nodiscard]] int getDeleteMessages() const;
|
[[nodiscard]] int getDeleteMessages() const;
|
||||||
void addWarningOption(const QString warning);
|
void addWarningOption(const QString warning, int startingIl = 1);
|
||||||
};
|
};
|
||||||
|
|
||||||
class AdminNotesDialog : public QDialog
|
class AdminNotesDialog : public QDialog
|
||||||
|
|
|
||||||
462
cockatrice/src/interface/widgets/tabs/tab_moderation.cpp
Normal file
462
cockatrice/src/interface/widgets/tabs/tab_moderation.cpp
Normal file
|
|
@ -0,0 +1,462 @@
|
||||||
|
#include "tab_moderation.h"
|
||||||
|
|
||||||
|
#include "abstract_client.h"
|
||||||
|
#include "tab_supervisor.h"
|
||||||
|
|
||||||
|
#include <QDateTime>
|
||||||
|
#include <QGridLayout>
|
||||||
|
#include <QGroupBox>
|
||||||
|
#include <QHBoxLayout>
|
||||||
|
#include <QHeaderView>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QLineEdit>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QPushButton>
|
||||||
|
#include <QSplitter>
|
||||||
|
#include <QTableWidget>
|
||||||
|
#include <QTextEdit>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
#include <libcockatrice/protocol/pb/command_report_user_info.pb.h>
|
||||||
|
#include <libcockatrice/protocol/pb/moderator_commands.pb.h>
|
||||||
|
#include <libcockatrice/protocol/pb/response_moderator_last_logins.pb.h>
|
||||||
|
#include <libcockatrice/protocol/pb/response_remove_user_avatar.pb.h>
|
||||||
|
#include <libcockatrice/protocol/pb/response_report_user_info.pb.h>
|
||||||
|
#include <libcockatrice/protocol/pb/response_reset_user_password.pb.h>
|
||||||
|
#include <libcockatrice/protocol/pb/response_user_alts.pb.h>
|
||||||
|
#include <libcockatrice/protocol/pb/response_user_sessions.pb.h>
|
||||||
|
#include <libcockatrice/protocol/pb/serverinfo_user.pb.h>
|
||||||
|
#include <libcockatrice/protocol/pending_command.h>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
constexpr int COL_ALTS_USER = 0;
|
||||||
|
constexpr int COL_ALTS_EMAIL = 1;
|
||||||
|
constexpr int COL_ALTS_CLIENTID = 2;
|
||||||
|
constexpr int COL_ALTS_REGISTERED = 3;
|
||||||
|
constexpr int COL_ALTS_LAST_LOGIN = 4;
|
||||||
|
constexpr int COL_ALTS_WARNS = 5;
|
||||||
|
constexpr int COL_ALTS_BANS = 6;
|
||||||
|
constexpr int COL_ALTS_ACTIVE = 7;
|
||||||
|
constexpr int COL_ALTS_COUNT = 8;
|
||||||
|
|
||||||
|
constexpr int COL_SESSIONS_IP = 0;
|
||||||
|
constexpr int COL_SESSIONS_CLIENTID = 1;
|
||||||
|
constexpr int COL_SESSIONS_START = 2;
|
||||||
|
constexpr int COL_SESSIONS_END = 3;
|
||||||
|
constexpr int COL_SESSIONS_TYPE = 4;
|
||||||
|
constexpr int COL_SESSIONS_COUNT = 5;
|
||||||
|
|
||||||
|
constexpr int COL_STAFF_USER = 0;
|
||||||
|
constexpr int COL_STAFF_LEVEL = 1;
|
||||||
|
constexpr int COL_STAFF_LAST_LOGIN = 2;
|
||||||
|
constexpr int COL_STAFF_COUNT = 3;
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TabModeration::TabModeration(TabSupervisor *_tabSupervisor, AbstractClient *_client, const QString &initialUser)
|
||||||
|
: Tab(_tabSupervisor), client(_client)
|
||||||
|
{
|
||||||
|
auto *centralWidget = new QWidget(this);
|
||||||
|
setCentralWidget(centralWidget);
|
||||||
|
|
||||||
|
searchEdit = new QLineEdit;
|
||||||
|
searchEdit->setClearButtonEnabled(true);
|
||||||
|
connect(searchEdit, &QLineEdit::returnPressed, this, &TabModeration::investigateUser);
|
||||||
|
|
||||||
|
investigateButton = new QPushButton;
|
||||||
|
connect(investigateButton, &QPushButton::clicked, this, &TabModeration::investigateUser);
|
||||||
|
|
||||||
|
resetPasswordButton = new QPushButton;
|
||||||
|
connect(resetPasswordButton, &QPushButton::clicked, this, &TabModeration::resetPassword);
|
||||||
|
|
||||||
|
removeAvatarButton = new QPushButton;
|
||||||
|
connect(removeAvatarButton, &QPushButton::clicked, this, &TabModeration::removeAvatar);
|
||||||
|
|
||||||
|
auto *topBar = new QHBoxLayout;
|
||||||
|
topBar->addWidget(searchEdit);
|
||||||
|
topBar->addWidget(investigateButton);
|
||||||
|
topBar->addStretch();
|
||||||
|
topBar->addWidget(resetPasswordButton);
|
||||||
|
topBar->addWidget(removeAvatarButton);
|
||||||
|
|
||||||
|
userInfoGroup = new QGroupBox;
|
||||||
|
userInfoNameLabel = new QLabel;
|
||||||
|
userInfoNameValue = new QLabel;
|
||||||
|
userInfoRegisteredLabel = new QLabel;
|
||||||
|
userInfoRegisteredValue = new QLabel;
|
||||||
|
userInfoLastLoginLabel = new QLabel;
|
||||||
|
userInfoLastLoginValue = new QLabel;
|
||||||
|
userInfoStatusLabel = new QLabel;
|
||||||
|
userInfoStatusValue = new QLabel;
|
||||||
|
userInfoCountsLabel = new QLabel;
|
||||||
|
userInfoCountsValue = new QLabel;
|
||||||
|
userInfoNotesLabel = new QLabel;
|
||||||
|
userInfoNotesEdit = new QTextEdit;
|
||||||
|
userInfoNotesEdit->setReadOnly(true);
|
||||||
|
|
||||||
|
auto *infoGrid = new QGridLayout;
|
||||||
|
infoGrid->addWidget(userInfoNameLabel, 0, 0);
|
||||||
|
infoGrid->addWidget(userInfoNameValue, 0, 1);
|
||||||
|
infoGrid->addWidget(userInfoRegisteredLabel, 0, 2);
|
||||||
|
infoGrid->addWidget(userInfoRegisteredValue, 0, 3);
|
||||||
|
infoGrid->addWidget(userInfoLastLoginLabel, 1, 0);
|
||||||
|
infoGrid->addWidget(userInfoLastLoginValue, 1, 1);
|
||||||
|
infoGrid->addWidget(userInfoStatusLabel, 1, 2);
|
||||||
|
infoGrid->addWidget(userInfoStatusValue, 1, 3);
|
||||||
|
infoGrid->addWidget(userInfoCountsLabel, 2, 0);
|
||||||
|
infoGrid->addWidget(userInfoCountsValue, 2, 1, 1, 3);
|
||||||
|
infoGrid->addWidget(userInfoNotesLabel, 3, 0, Qt::AlignTop);
|
||||||
|
infoGrid->addWidget(userInfoNotesEdit, 3, 1, 1, 3);
|
||||||
|
infoGrid->setColumnStretch(1, 1);
|
||||||
|
infoGrid->setColumnStretch(3, 1);
|
||||||
|
|
||||||
|
auto *infoLayout = new QVBoxLayout(userInfoGroup);
|
||||||
|
infoLayout->addLayout(infoGrid);
|
||||||
|
|
||||||
|
auto configureTable = [](QTableWidget *table) {
|
||||||
|
table->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||||
|
table->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||||
|
table->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||||
|
table->verticalHeader()->setVisible(false);
|
||||||
|
table->setAlternatingRowColors(true);
|
||||||
|
table->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
|
||||||
|
table->horizontalHeader()->setStretchLastSection(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
altsGroup = new QGroupBox;
|
||||||
|
altsTable = new QTableWidget(0, COL_ALTS_COUNT);
|
||||||
|
configureTable(altsTable);
|
||||||
|
auto *altsLayout = new QVBoxLayout(altsGroup);
|
||||||
|
altsLayout->addWidget(altsTable);
|
||||||
|
|
||||||
|
sessionsGroup = new QGroupBox;
|
||||||
|
sessionsTable = new QTableWidget(0, COL_SESSIONS_COUNT);
|
||||||
|
configureTable(sessionsTable);
|
||||||
|
auto *sessionsLayout = new QVBoxLayout(sessionsGroup);
|
||||||
|
sessionsLayout->addWidget(sessionsTable);
|
||||||
|
|
||||||
|
staffGroup = new QGroupBox;
|
||||||
|
staffTable = new QTableWidget(0, COL_STAFF_COUNT);
|
||||||
|
configureTable(staffTable);
|
||||||
|
refreshStaffButton = new QPushButton;
|
||||||
|
connect(refreshStaffButton, &QPushButton::clicked, this, &TabModeration::requestModeratorLogins);
|
||||||
|
auto *staffHeader = new QHBoxLayout;
|
||||||
|
staffHeader->addStretch();
|
||||||
|
staffHeader->addWidget(refreshStaffButton);
|
||||||
|
auto *staffLayout = new QVBoxLayout(staffGroup);
|
||||||
|
staffLayout->addWidget(staffTable);
|
||||||
|
staffLayout->addLayout(staffHeader);
|
||||||
|
|
||||||
|
auto *splitter = new QSplitter(Qt::Vertical);
|
||||||
|
splitter->addWidget(userInfoGroup);
|
||||||
|
splitter->addWidget(altsGroup);
|
||||||
|
splitter->addWidget(sessionsGroup);
|
||||||
|
splitter->addWidget(staffGroup);
|
||||||
|
splitter->setStretchFactor(0, 1);
|
||||||
|
splitter->setStretchFactor(1, 2);
|
||||||
|
splitter->setStretchFactor(2, 2);
|
||||||
|
splitter->setStretchFactor(3, 1);
|
||||||
|
|
||||||
|
auto *mainLayout = new QVBoxLayout(centralWidget);
|
||||||
|
mainLayout->addLayout(topBar);
|
||||||
|
mainLayout->addWidget(splitter);
|
||||||
|
|
||||||
|
retranslateUi();
|
||||||
|
clearUserData();
|
||||||
|
requestModeratorLogins();
|
||||||
|
investigate(initialUser);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TabModeration::retranslateUi()
|
||||||
|
{
|
||||||
|
searchEdit->setPlaceholderText(tr("User name"));
|
||||||
|
investigateButton->setText(tr("Investigate"));
|
||||||
|
resetPasswordButton->setText(tr("Reset Password"));
|
||||||
|
removeAvatarButton->setText(tr("Remove Avatar"));
|
||||||
|
refreshStaffButton->setText(tr("Refresh"));
|
||||||
|
|
||||||
|
userInfoGroup->setTitle(tr("User Info"));
|
||||||
|
userInfoNameLabel->setText(tr("Name:"));
|
||||||
|
userInfoRegisteredLabel->setText(tr("Registered:"));
|
||||||
|
userInfoLastLoginLabel->setText(tr("Last login:"));
|
||||||
|
userInfoStatusLabel->setText(tr("Status:"));
|
||||||
|
userInfoCountsLabel->setText(tr("Counts:"));
|
||||||
|
userInfoNotesLabel->setText(tr("Admin notes:"));
|
||||||
|
|
||||||
|
altsGroup->setTitle(tr("Alts"));
|
||||||
|
sessionsGroup->setTitle(tr("Sessions"));
|
||||||
|
staffGroup->setTitle(tr("Staff Last Logins"));
|
||||||
|
|
||||||
|
altsTable->setHorizontalHeaderLabels({tr("User"), tr("eMail"), tr("Client ID"), tr("Registered"), tr("Last login"),
|
||||||
|
tr("Warns"), tr("Bans"), tr("Active")});
|
||||||
|
sessionsTable->setHorizontalHeaderLabels({tr("IP"), tr("Client ID"), tr("Start"), tr("End"), tr("Type")});
|
||||||
|
staffTable->setHorizontalHeaderLabels({tr("User"), tr("Level"), tr("Last login")});
|
||||||
|
}
|
||||||
|
|
||||||
|
QString TabModeration::formatEpoch(quint64 ts) const
|
||||||
|
{
|
||||||
|
if (ts == 0) {
|
||||||
|
return tr("Unknown");
|
||||||
|
}
|
||||||
|
return QDateTime::fromSecsSinceEpoch(ts).toLocalTime().toString("yyyy-MM-dd HH:mm");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TabModeration::clearUserData()
|
||||||
|
{
|
||||||
|
currentUser.clear();
|
||||||
|
userInfoNameValue->clear();
|
||||||
|
userInfoRegisteredValue->clear();
|
||||||
|
userInfoLastLoginValue->clear();
|
||||||
|
userInfoStatusValue->clear();
|
||||||
|
userInfoCountsValue->clear();
|
||||||
|
userInfoNotesEdit->clear();
|
||||||
|
altsTable->setRowCount(0);
|
||||||
|
sessionsTable->setRowCount(0);
|
||||||
|
resetPasswordButton->setEnabled(false);
|
||||||
|
removeAvatarButton->setEnabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TabModeration::investigate(const QString &userName)
|
||||||
|
{
|
||||||
|
if (userName.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
searchEdit->setText(userName);
|
||||||
|
investigateUser();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TabModeration::investigateUser()
|
||||||
|
{
|
||||||
|
const QString userName = searchEdit->text().simplified();
|
||||||
|
if (userName.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
currentUser = userName;
|
||||||
|
resetPasswordButton->setEnabled(true);
|
||||||
|
removeAvatarButton->setEnabled(true);
|
||||||
|
altsTable->setRowCount(0);
|
||||||
|
sessionsTable->setRowCount(0);
|
||||||
|
requestUserInfo(userName);
|
||||||
|
requestSessions(userName);
|
||||||
|
requestAlts(userName);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TabModeration::requestUserInfo(const QString &userName)
|
||||||
|
{
|
||||||
|
userInfoNameValue->setText(userName);
|
||||||
|
userInfoRegisteredValue->setText(tr("Loading..."));
|
||||||
|
userInfoLastLoginValue->setText(tr("Loading..."));
|
||||||
|
userInfoStatusValue->clear();
|
||||||
|
userInfoCountsValue->clear();
|
||||||
|
userInfoNotesEdit->clear();
|
||||||
|
|
||||||
|
Command_ReportUserInfo cmd;
|
||||||
|
cmd.set_user_name(userName.toStdString());
|
||||||
|
PendingCommand *pend = client->prepareModeratorCommand(cmd);
|
||||||
|
connect(pend, &PendingCommand::finished, this, &TabModeration::userInfoResponse);
|
||||||
|
client->sendCommand(pend);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TabModeration::userInfoResponse(const Response &response)
|
||||||
|
{
|
||||||
|
if (response.response_code() != Response::RespOk) {
|
||||||
|
userInfoRegisteredValue->clear();
|
||||||
|
userInfoLastLoginValue->clear();
|
||||||
|
userInfoStatusValue->setText(tr("Error loading user info."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Response_ReportUserInfo &resp = response.GetExtension(Response_ReportUserInfo::ext);
|
||||||
|
if (resp.has_user_name() && QString::fromStdString(resp.user_name()) != currentUser) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
userInfoRegisteredValue->setText(formatEpoch(resp.registration_time()));
|
||||||
|
userInfoLastLoginValue->setText(formatEpoch(resp.last_login()));
|
||||||
|
|
||||||
|
QStringList statusParts;
|
||||||
|
statusParts << (resp.is_active() ? tr("active") : tr("inactive"));
|
||||||
|
if (resp.has_is_admin() && resp.is_admin()) {
|
||||||
|
statusParts << tr("admin");
|
||||||
|
}
|
||||||
|
userInfoStatusValue->setText(statusParts.join(", "));
|
||||||
|
|
||||||
|
userInfoCountsValue->setText(tr("Reports: %1 Bans: %2 Warnings: %3")
|
||||||
|
.arg(resp.total_reports())
|
||||||
|
.arg(resp.total_bans())
|
||||||
|
.arg(resp.total_warns()));
|
||||||
|
|
||||||
|
if (resp.has_admin_notes() && !resp.admin_notes().empty()) {
|
||||||
|
userInfoNotesEdit->setPlainText(QString::fromStdString(resp.admin_notes()));
|
||||||
|
} else {
|
||||||
|
userInfoNotesEdit->setPlainText(tr("(none)"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TabModeration::requestSessions(const QString &userName)
|
||||||
|
{
|
||||||
|
Command_GetUserSessions cmd;
|
||||||
|
cmd.set_user_name(userName.toStdString());
|
||||||
|
PendingCommand *pend = client->prepareModeratorCommand(cmd);
|
||||||
|
connect(pend, &PendingCommand::finished, this, &TabModeration::sessionsResponse);
|
||||||
|
client->sendCommand(pend);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TabModeration::sessionsResponse(const Response &response)
|
||||||
|
{
|
||||||
|
sessionsTable->setRowCount(0);
|
||||||
|
if (response.response_code() != Response::RespOk) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Response_UserSessions &resp = response.GetExtension(Response_UserSessions::ext);
|
||||||
|
sessionsTable->setRowCount(resp.sessions_size());
|
||||||
|
for (int i = 0; i < resp.sessions_size(); ++i) {
|
||||||
|
const ServerInfo_UserSession &session = resp.sessions(i);
|
||||||
|
sessionsTable->setItem(i, COL_SESSIONS_IP, new QTableWidgetItem(QString::fromStdString(session.ip_address())));
|
||||||
|
sessionsTable->setItem(i, COL_SESSIONS_CLIENTID,
|
||||||
|
new QTableWidgetItem(QString::fromStdString(session.clientid())));
|
||||||
|
sessionsTable->setItem(i, COL_SESSIONS_START, new QTableWidgetItem(formatEpoch(session.start_time())));
|
||||||
|
sessionsTable->setItem(
|
||||||
|
i, COL_SESSIONS_END,
|
||||||
|
new QTableWidgetItem(session.end_time() == 0 ? tr("Active") : formatEpoch(session.end_time())));
|
||||||
|
sessionsTable->setItem(i, COL_SESSIONS_TYPE,
|
||||||
|
new QTableWidgetItem(QString::fromStdString(session.connection_type())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TabModeration::requestAlts(const QString &userName)
|
||||||
|
{
|
||||||
|
Command_GetUserAlts cmd;
|
||||||
|
cmd.set_user_name(userName.toStdString());
|
||||||
|
PendingCommand *pend = client->prepareModeratorCommand(cmd);
|
||||||
|
connect(pend, &PendingCommand::finished, this, &TabModeration::altsResponse);
|
||||||
|
client->sendCommand(pend);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TabModeration::altsResponse(const Response &response)
|
||||||
|
{
|
||||||
|
altsTable->setRowCount(0);
|
||||||
|
if (response.response_code() != Response::RespOk) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Response_UserAlts &resp = response.GetExtension(Response_UserAlts::ext);
|
||||||
|
altsTable->setRowCount(resp.alts_size());
|
||||||
|
for (int i = 0; i < resp.alts_size(); ++i) {
|
||||||
|
const ServerInfo_UserAlt &alt = resp.alts(i);
|
||||||
|
altsTable->setItem(i, COL_ALTS_USER, new QTableWidgetItem(QString::fromStdString(alt.user_name())));
|
||||||
|
altsTable->setItem(i, COL_ALTS_EMAIL, new QTableWidgetItem(QString::fromStdString(alt.email())));
|
||||||
|
altsTable->setItem(i, COL_ALTS_CLIENTID, new QTableWidgetItem(QString::fromStdString(alt.clientid())));
|
||||||
|
altsTable->setItem(i, COL_ALTS_REGISTERED, new QTableWidgetItem(formatEpoch(alt.registration_time())));
|
||||||
|
altsTable->setItem(i, COL_ALTS_LAST_LOGIN, new QTableWidgetItem(formatEpoch(alt.last_login())));
|
||||||
|
altsTable->setItem(i, COL_ALTS_WARNS, new QTableWidgetItem(QString::number(alt.warn_count())));
|
||||||
|
altsTable->setItem(i, COL_ALTS_BANS, new QTableWidgetItem(QString::number(alt.ban_count())));
|
||||||
|
altsTable->setItem(i, COL_ALTS_ACTIVE, new QTableWidgetItem(alt.is_active() ? tr("yes") : tr("no")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TabModeration::requestModeratorLogins()
|
||||||
|
{
|
||||||
|
Command_GetModeratorLastLogins cmd;
|
||||||
|
PendingCommand *pend = client->prepareModeratorCommand(cmd);
|
||||||
|
connect(pend, &PendingCommand::finished, this, &TabModeration::moderatorLoginsResponse);
|
||||||
|
client->sendCommand(pend);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TabModeration::moderatorLoginsResponse(const Response &response)
|
||||||
|
{
|
||||||
|
staffTable->setRowCount(0);
|
||||||
|
if (response.response_code() != Response::RespOk) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Response_ModeratorLastLogins &resp = response.GetExtension(Response_ModeratorLastLogins::ext);
|
||||||
|
staffTable->setRowCount(resp.logins_size());
|
||||||
|
for (int i = 0; i < resp.logins_size(); ++i) {
|
||||||
|
const ServerInfo_ModeratorLogin &login = resp.logins(i);
|
||||||
|
staffTable->setItem(i, COL_STAFF_USER, new QTableWidgetItem(QString::fromStdString(login.user_name())));
|
||||||
|
staffTable->setItem(i, COL_STAFF_LAST_LOGIN, new QTableWidgetItem(formatEpoch(login.last_login())));
|
||||||
|
|
||||||
|
QStringList levels;
|
||||||
|
if (login.user_level() & ServerInfo_User::IsAdmin) {
|
||||||
|
levels << tr("Admin");
|
||||||
|
}
|
||||||
|
if (login.user_level() & ServerInfo_User::IsModerator) {
|
||||||
|
levels << tr("Moderator");
|
||||||
|
}
|
||||||
|
if (login.user_level() & ServerInfo_User::IsJudge) {
|
||||||
|
levels << tr("Judge");
|
||||||
|
}
|
||||||
|
staffTable->setItem(i, COL_STAFF_LEVEL, new QTableWidgetItem(levels.join(" / ")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TabModeration::resetPassword()
|
||||||
|
{
|
||||||
|
if (currentUser.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QMessageBox::StandardButton choice =
|
||||||
|
QMessageBox::warning(this, tr("Reset Password"),
|
||||||
|
tr("Reset the password of %1? A temporary password will be generated and shown to you. "
|
||||||
|
"The user must change it on their first login.")
|
||||||
|
.arg(currentUser),
|
||||||
|
QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Cancel);
|
||||||
|
if (choice != QMessageBox::Ok) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Command_ResetUserPassword cmd;
|
||||||
|
cmd.set_user_name(currentUser.toStdString());
|
||||||
|
PendingCommand *pend = client->prepareModeratorCommand(cmd);
|
||||||
|
connect(pend, &PendingCommand::finished, this, &TabModeration::resetPasswordResponse);
|
||||||
|
client->sendCommand(pend);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TabModeration::resetPasswordResponse(const Response &response)
|
||||||
|
{
|
||||||
|
if (response.response_code() != Response::RespOk) {
|
||||||
|
QMessageBox::critical(this, tr("Error"), tr("Password reset failed."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Response_ResetUserPassword &resp = response.GetExtension(Response_ResetUserPassword::ext);
|
||||||
|
QMessageBox::information(this, tr("Password reset"),
|
||||||
|
tr("Temporary password for %1:\n\n%2\n\nPass it to the user through a secure channel.")
|
||||||
|
.arg(QString::fromStdString(resp.user_name()))
|
||||||
|
.arg(QString::fromStdString(resp.temporary_password())));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TabModeration::removeAvatar()
|
||||||
|
{
|
||||||
|
if (currentUser.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QMessageBox::StandardButton choice =
|
||||||
|
QMessageBox::warning(this, tr("Remove Avatar"),
|
||||||
|
tr("Remove the avatar of %1? The user will have to upload a new one.").arg(currentUser),
|
||||||
|
QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Cancel);
|
||||||
|
if (choice != QMessageBox::Ok) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Command_RemoveUserAvatar cmd;
|
||||||
|
cmd.set_user_name(currentUser.toStdString());
|
||||||
|
PendingCommand *pend = client->prepareModeratorCommand(cmd);
|
||||||
|
connect(pend, &PendingCommand::finished, this, &TabModeration::removeAvatarResponse);
|
||||||
|
client->sendCommand(pend);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TabModeration::removeAvatarResponse(const Response &response)
|
||||||
|
{
|
||||||
|
if (response.response_code() != Response::RespOk) {
|
||||||
|
QMessageBox::critical(this, tr("Error"), tr("Could not remove the avatar."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Response_RemoveUserAvatar &resp = response.GetExtension(Response_RemoveUserAvatar::ext);
|
||||||
|
QMessageBox::information(this, tr("Avatar removed"),
|
||||||
|
tr("The avatar of %1 has been removed.").arg(QString::fromStdString(resp.user_name())));
|
||||||
|
}
|
||||||
83
cockatrice/src/interface/widgets/tabs/tab_moderation.h
Normal file
83
cockatrice/src/interface/widgets/tabs/tab_moderation.h
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
#ifndef TAB_MODERATION_H
|
||||||
|
#define TAB_MODERATION_H
|
||||||
|
|
||||||
|
#include "tab.h"
|
||||||
|
|
||||||
|
#include <libcockatrice/protocol/pb/response.pb.h>
|
||||||
|
|
||||||
|
class AbstractClient;
|
||||||
|
class QGroupBox;
|
||||||
|
class QLabel;
|
||||||
|
class QLineEdit;
|
||||||
|
class QPushButton;
|
||||||
|
class QTableWidget;
|
||||||
|
class QTextEdit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Staff investigation tool. Lets moderators look up a user's account data,
|
||||||
|
* alternate accounts, login sessions, and staff login activity, and offers
|
||||||
|
* the password-reset and remove-avatar actions.
|
||||||
|
*/
|
||||||
|
class TabModeration : public Tab
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit TabModeration(TabSupervisor *_tabSupervisor, AbstractClient *_client, const QString &initialUser = {});
|
||||||
|
void retranslateUi() override;
|
||||||
|
[[nodiscard]] QString getTabText() const override
|
||||||
|
{
|
||||||
|
return tr("Moderation");
|
||||||
|
}
|
||||||
|
void investigate(const QString &userName);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void investigateUser();
|
||||||
|
void userInfoResponse(const Response &response);
|
||||||
|
void sessionsResponse(const Response &response);
|
||||||
|
void altsResponse(const Response &response);
|
||||||
|
void moderatorLoginsResponse(const Response &response);
|
||||||
|
void resetPassword();
|
||||||
|
void resetPasswordResponse(const Response &response);
|
||||||
|
void removeAvatar();
|
||||||
|
void removeAvatarResponse(const Response &response);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void requestUserInfo(const QString &userName);
|
||||||
|
void requestSessions(const QString &userName);
|
||||||
|
void requestAlts(const QString &userName);
|
||||||
|
void requestModeratorLogins();
|
||||||
|
void clearUserData();
|
||||||
|
[[nodiscard]] QString formatEpoch(quint64 ts) const;
|
||||||
|
|
||||||
|
AbstractClient *client;
|
||||||
|
QString currentUser;
|
||||||
|
|
||||||
|
QLineEdit *searchEdit;
|
||||||
|
QPushButton *investigateButton;
|
||||||
|
QPushButton *resetPasswordButton;
|
||||||
|
QPushButton *removeAvatarButton;
|
||||||
|
|
||||||
|
QGroupBox *userInfoGroup;
|
||||||
|
QLabel *userInfoNameLabel;
|
||||||
|
QLabel *userInfoNameValue;
|
||||||
|
QLabel *userInfoRegisteredLabel;
|
||||||
|
QLabel *userInfoRegisteredValue;
|
||||||
|
QLabel *userInfoLastLoginLabel;
|
||||||
|
QLabel *userInfoLastLoginValue;
|
||||||
|
QLabel *userInfoStatusLabel;
|
||||||
|
QLabel *userInfoStatusValue;
|
||||||
|
QLabel *userInfoCountsLabel;
|
||||||
|
QLabel *userInfoCountsValue;
|
||||||
|
QLabel *userInfoNotesLabel;
|
||||||
|
QTextEdit *userInfoNotesEdit;
|
||||||
|
|
||||||
|
QGroupBox *altsGroup;
|
||||||
|
QTableWidget *altsTable;
|
||||||
|
QGroupBox *sessionsGroup;
|
||||||
|
QTableWidget *sessionsTable;
|
||||||
|
QGroupBox *staffGroup;
|
||||||
|
QTableWidget *staffTable;
|
||||||
|
QPushButton *refreshStaffButton;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // TAB_MODERATION_H
|
||||||
|
|
@ -19,6 +19,7 @@
|
||||||
#include "tab_home.h"
|
#include "tab_home.h"
|
||||||
#include "tab_logs.h"
|
#include "tab_logs.h"
|
||||||
#include "tab_message.h"
|
#include "tab_message.h"
|
||||||
|
#include "tab_moderation.h"
|
||||||
#include "tab_replays.h"
|
#include "tab_replays.h"
|
||||||
#include "tab_report.h"
|
#include "tab_report.h"
|
||||||
#include "tab_room.h"
|
#include "tab_room.h"
|
||||||
|
|
@ -117,7 +118,7 @@ void CloseButton::paintEvent(QPaintEvent * /*event*/)
|
||||||
TabSupervisor::TabSupervisor(AbstractClient *_client, QMenu *tabsMenu, QWidget *parent)
|
TabSupervisor::TabSupervisor(AbstractClient *_client, QMenu *tabsMenu, QWidget *parent)
|
||||||
: QTabWidget(parent), userInfo(nullptr), client(_client), tabsMenu(tabsMenu), tabVisualDeckStorage(nullptr),
|
: QTabWidget(parent), userInfo(nullptr), client(_client), tabsMenu(tabsMenu), tabVisualDeckStorage(nullptr),
|
||||||
tabServer(nullptr), tabAccount(nullptr), tabDeckStorage(nullptr), tabReplays(nullptr), tabAdmin(nullptr),
|
tabServer(nullptr), tabAccount(nullptr), tabDeckStorage(nullptr), tabReplays(nullptr), tabAdmin(nullptr),
|
||||||
tabLog(nullptr), tabReport(nullptr), isLocalGame(false)
|
tabLog(nullptr), tabReport(nullptr), tabModeration(nullptr), isLocalGame(false)
|
||||||
{
|
{
|
||||||
setElideMode(Qt::ElideRight);
|
setElideMode(Qt::ElideRight);
|
||||||
setMovable(true);
|
setMovable(true);
|
||||||
|
|
@ -198,6 +199,10 @@ TabSupervisor::TabSupervisor(AbstractClient *_client, QMenu *tabsMenu, QWidget *
|
||||||
aTabReport->setCheckable(true);
|
aTabReport->setCheckable(true);
|
||||||
connect(aTabReport, &QAction::triggered, this, &TabSupervisor::actTabReport);
|
connect(aTabReport, &QAction::triggered, this, &TabSupervisor::actTabReport);
|
||||||
|
|
||||||
|
aTabModeration = new QAction(this);
|
||||||
|
aTabModeration->setCheckable(true);
|
||||||
|
connect(aTabModeration, &QAction::triggered, this, &TabSupervisor::actTabModeration);
|
||||||
|
|
||||||
connect(&SettingsCache::instance().shortcuts(), &ShortcutsSettings::shortCutChanged, this,
|
connect(&SettingsCache::instance().shortcuts(), &ShortcutsSettings::shortCutChanged, this,
|
||||||
&TabSupervisor::refreshShortcuts);
|
&TabSupervisor::refreshShortcuts);
|
||||||
refreshShortcuts();
|
refreshShortcuts();
|
||||||
|
|
@ -238,6 +243,7 @@ void TabSupervisor::retranslateUi()
|
||||||
aTabAdmin->setText(tr("Administration"));
|
aTabAdmin->setText(tr("Administration"));
|
||||||
aTabLog->setText(tr("Logs"));
|
aTabLog->setText(tr("Logs"));
|
||||||
aTabReport->setText(tr("Report Queue"));
|
aTabReport->setText(tr("Report Queue"));
|
||||||
|
aTabModeration->setText(tr("Moderation"));
|
||||||
|
|
||||||
// tabs
|
// tabs
|
||||||
QList<Tab *> tabs;
|
QList<Tab *> tabs;
|
||||||
|
|
@ -248,6 +254,7 @@ void TabSupervisor::retranslateUi()
|
||||||
tabs.append(tabAccount);
|
tabs.append(tabAccount);
|
||||||
tabs.append(tabLog);
|
tabs.append(tabLog);
|
||||||
tabs.append(tabReport);
|
tabs.append(tabReport);
|
||||||
|
tabs.append(tabModeration);
|
||||||
QMapIterator<int, TabRoom *> roomIterator(roomTabs);
|
QMapIterator<int, TabRoom *> roomIterator(roomTabs);
|
||||||
while (roomIterator.hasNext()) {
|
while (roomIterator.hasNext()) {
|
||||||
tabs.append(roomIterator.next().value());
|
tabs.append(roomIterator.next().value());
|
||||||
|
|
@ -295,6 +302,7 @@ void TabSupervisor::refreshShortcuts()
|
||||||
aTabAdmin->setShortcuts(shortcuts.getShortcut("Tabs/aTabAdmin"));
|
aTabAdmin->setShortcuts(shortcuts.getShortcut("Tabs/aTabAdmin"));
|
||||||
aTabLog->setShortcuts(shortcuts.getShortcut("Tabs/aTabLog"));
|
aTabLog->setShortcuts(shortcuts.getShortcut("Tabs/aTabLog"));
|
||||||
aTabReport->setShortcuts(shortcuts.getShortcut("Tabs/aTabReport"));
|
aTabReport->setShortcuts(shortcuts.getShortcut("Tabs/aTabReport"));
|
||||||
|
aTabModeration->setShortcuts(shortcuts.getShortcut("Tabs/aTabModeration"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void TabSupervisor::closeEvent(QCloseEvent *event)
|
void TabSupervisor::closeEvent(QCloseEvent *event)
|
||||||
|
|
@ -497,6 +505,7 @@ void TabSupervisor::start(const ServerInfo_User &_userInfo)
|
||||||
tabsMenu->addAction(aTabLog);
|
tabsMenu->addAction(aTabLog);
|
||||||
tabsMenu->addAction(aTabCardArtRules);
|
tabsMenu->addAction(aTabCardArtRules);
|
||||||
tabsMenu->addAction(aTabReport);
|
tabsMenu->addAction(aTabReport);
|
||||||
|
tabsMenu->addAction(aTabModeration);
|
||||||
|
|
||||||
if (SettingsCache::instance().tabs().getTabAdminOpen()) {
|
if (SettingsCache::instance().tabs().getTabAdminOpen()) {
|
||||||
openTabAdmin();
|
openTabAdmin();
|
||||||
|
|
@ -507,6 +516,9 @@ void TabSupervisor::start(const ServerInfo_User &_userInfo)
|
||||||
if (SettingsCache::instance().tabs().getTabReportOpen()) {
|
if (SettingsCache::instance().tabs().getTabReportOpen()) {
|
||||||
openTabReport();
|
openTabReport();
|
||||||
}
|
}
|
||||||
|
if (SettingsCache::instance().tabs().getTabModerationOpen()) {
|
||||||
|
openTabModeration();
|
||||||
|
}
|
||||||
openTabCardArtRules();
|
openTabCardArtRules();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -521,6 +533,7 @@ void TabSupervisor::startLocal(const QList<AbstractClient *> &_clients)
|
||||||
tabAdmin = nullptr;
|
tabAdmin = nullptr;
|
||||||
tabLog = nullptr;
|
tabLog = nullptr;
|
||||||
tabReport = nullptr;
|
tabReport = nullptr;
|
||||||
|
tabModeration = nullptr;
|
||||||
isLocalGame = true;
|
isLocalGame = true;
|
||||||
userInfo = new ServerInfo_User;
|
userInfo = new ServerInfo_User;
|
||||||
localClients = _clients;
|
localClients = _clients;
|
||||||
|
|
@ -565,6 +578,9 @@ void TabSupervisor::stop()
|
||||||
if (tabReport) {
|
if (tabReport) {
|
||||||
tabReport->close();
|
tabReport->close();
|
||||||
}
|
}
|
||||||
|
if (tabModeration) {
|
||||||
|
tabModeration->close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<Tab *> tabsToDelete;
|
QList<Tab *> tabsToDelete;
|
||||||
|
|
@ -826,6 +842,35 @@ void TabSupervisor::openTabReport()
|
||||||
aTabReport->setChecked(true);
|
aTabReport->setChecked(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TabSupervisor::actTabModeration(bool checked)
|
||||||
|
{
|
||||||
|
SettingsCache::instance().tabs().setTabModerationOpen(checked);
|
||||||
|
if (checked && !tabModeration) {
|
||||||
|
openTabModeration();
|
||||||
|
setCurrentWidget(tabModeration);
|
||||||
|
} else if (!checked && tabModeration) {
|
||||||
|
tabModeration->closeRequest();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TabSupervisor::openTabModeration(const QString &userName)
|
||||||
|
{
|
||||||
|
if (tabModeration) {
|
||||||
|
setCurrentWidget(tabModeration);
|
||||||
|
if (!userName.isEmpty()) {
|
||||||
|
tabModeration->investigate(userName);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tabModeration = new TabModeration(this, client, userName);
|
||||||
|
myAddTab(tabModeration, aTabModeration);
|
||||||
|
connect(tabModeration, &QObject::destroyed, this, [this] {
|
||||||
|
tabModeration = nullptr;
|
||||||
|
aTabModeration->setChecked(false);
|
||||||
|
});
|
||||||
|
aTabModeration->setChecked(true);
|
||||||
|
}
|
||||||
|
|
||||||
void TabSupervisor::updatePingTime(int value, int max)
|
void TabSupervisor::updatePingTime(int value, int max)
|
||||||
{
|
{
|
||||||
if (!tabServer) {
|
if (!tabServer) {
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ class TabReplays;
|
||||||
class TabAdmin;
|
class TabAdmin;
|
||||||
class TabMessage;
|
class TabMessage;
|
||||||
class TabReport;
|
class TabReport;
|
||||||
|
class TabModeration;
|
||||||
class TabAccount;
|
class TabAccount;
|
||||||
class TabDeckEditor;
|
class TabDeckEditor;
|
||||||
class TabLog;
|
class TabLog;
|
||||||
|
|
@ -105,6 +106,7 @@ private:
|
||||||
TabCardArtRules *tabCardArtRules;
|
TabCardArtRules *tabCardArtRules;
|
||||||
TabLog *tabLog;
|
TabLog *tabLog;
|
||||||
TabReport *tabReport;
|
TabReport *tabReport;
|
||||||
|
TabModeration *tabModeration;
|
||||||
QMap<int, TabRoom *> roomTabs;
|
QMap<int, TabRoom *> roomTabs;
|
||||||
QMap<int, TabGame *> gameTabs;
|
QMap<int, TabGame *> gameTabs;
|
||||||
QList<TabGame *> replayTabs;
|
QList<TabGame *> replayTabs;
|
||||||
|
|
@ -114,7 +116,7 @@ private:
|
||||||
|
|
||||||
QAction *aTabHome, *aTabDeckEditor, *aTabVisualDeckEditor, *aTabEdhRec, *aTabArchidekt, *aTabVisualDeckStorage,
|
QAction *aTabHome, *aTabDeckEditor, *aTabVisualDeckEditor, *aTabEdhRec, *aTabArchidekt, *aTabVisualDeckStorage,
|
||||||
*aTabVisualDatabaseDisplay, *aTabServer, *aTabAccount, *aTabDeckStorage, *aTabReplays, *aTabAdmin,
|
*aTabVisualDatabaseDisplay, *aTabServer, *aTabAccount, *aTabDeckStorage, *aTabReplays, *aTabAdmin,
|
||||||
*aTabCardArtRules, *aTabLog, *aTabReport;
|
*aTabCardArtRules, *aTabLog, *aTabReport, *aTabModeration;
|
||||||
|
|
||||||
int myAddTab(Tab *tab, QAction *manager = nullptr);
|
int myAddTab(Tab *tab, QAction *manager = nullptr);
|
||||||
void addCloseButtonToTab(Tab *tab, int tabIndex, QAction *manager);
|
void addCloseButtonToTab(Tab *tab, int tabIndex, QAction *manager);
|
||||||
|
|
@ -186,6 +188,7 @@ public slots:
|
||||||
TabEdhRec *addEdhrecTab(const CardInfoPtr &cardToQuery, bool isCommander = false);
|
TabEdhRec *addEdhrecTab(const CardInfoPtr &cardToQuery, bool isCommander = false);
|
||||||
void openReplay(GameReplay *replay);
|
void openReplay(GameReplay *replay);
|
||||||
void joinReportGame(int gameId, int roomId);
|
void joinReportGame(int gameId, int roomId);
|
||||||
|
void openTabModeration(const QString &userName = {});
|
||||||
void switchToFirstAvailableNetworkTab();
|
void switchToFirstAvailableNetworkTab();
|
||||||
void maximizeMainWindow();
|
void maximizeMainWindow();
|
||||||
void actTabVisualDeckStorage(bool checked);
|
void actTabVisualDeckStorage(bool checked);
|
||||||
|
|
@ -202,6 +205,7 @@ private slots:
|
||||||
void actTabAdmin(bool checked);
|
void actTabAdmin(bool checked);
|
||||||
void actTabLog(bool checked);
|
void actTabLog(bool checked);
|
||||||
void actTabReport(bool checked);
|
void actTabReport(bool checked);
|
||||||
|
void actTabModeration(bool checked);
|
||||||
|
|
||||||
void openTabVisualDeckStorage();
|
void openTabVisualDeckStorage();
|
||||||
void openTabHome();
|
void openTabHome();
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ public:
|
||||||
[[nodiscard]] virtual bool getTabAdminOpen() const = 0;
|
[[nodiscard]] virtual bool getTabAdminOpen() const = 0;
|
||||||
[[nodiscard]] virtual bool getTabLogOpen() const = 0;
|
[[nodiscard]] virtual bool getTabLogOpen() const = 0;
|
||||||
[[nodiscard]] virtual bool getTabReportOpen() const = 0;
|
[[nodiscard]] virtual bool getTabReportOpen() const = 0;
|
||||||
|
[[nodiscard]] virtual bool getTabModerationOpen() const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // COCKATRICE_INTERFACE_TABS_SETTINGS_PROVIDER_H
|
#endif // COCKATRICE_INTERFACE_TABS_SETTINGS_PROVIDER_H
|
||||||
|
|
|
||||||
|
|
@ -101,6 +101,11 @@ bool TabsSettings::getTabReportOpen() const
|
||||||
return getValue("report", QString(), QString(), false).toBool();
|
return getValue("report", QString(), QString(), false).toBool();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool TabsSettings::getTabModerationOpen() const
|
||||||
|
{
|
||||||
|
return getValue("moderation", QString(), QString(), false).toBool();
|
||||||
|
}
|
||||||
|
|
||||||
void TabsSettings::setTabVisualDeckStorageOpen(bool value)
|
void TabsSettings::setTabVisualDeckStorageOpen(bool value)
|
||||||
{
|
{
|
||||||
setValue(value, "visualDeckStorage");
|
setValue(value, "visualDeckStorage");
|
||||||
|
|
@ -140,3 +145,8 @@ void TabsSettings::setTabReportOpen(bool value)
|
||||||
{
|
{
|
||||||
setValue(value, "report");
|
setValue(value, "report");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TabsSettings::setTabModerationOpen(bool value)
|
||||||
|
{
|
||||||
|
setValue(value, "moderation");
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@ public:
|
||||||
[[nodiscard]] bool getTabAdminOpen() const override;
|
[[nodiscard]] bool getTabAdminOpen() const override;
|
||||||
[[nodiscard]] bool getTabLogOpen() const override;
|
[[nodiscard]] bool getTabLogOpen() const override;
|
||||||
[[nodiscard]] bool getTabReportOpen() const override;
|
[[nodiscard]] bool getTabReportOpen() const override;
|
||||||
|
[[nodiscard]] bool getTabModerationOpen() const override;
|
||||||
|
|
||||||
void setStartupTabIndex(int value);
|
void setStartupTabIndex(int value);
|
||||||
void setStartupServerHost(const QString &host);
|
void setStartupServerHost(const QString &host);
|
||||||
|
|
@ -55,6 +56,7 @@ public:
|
||||||
void setTabAdminOpen(bool value);
|
void setTabAdminOpen(bool value);
|
||||||
void setTabLogOpen(bool value);
|
void setTabLogOpen(bool value);
|
||||||
void setTabReportOpen(bool value);
|
void setTabReportOpen(bool value);
|
||||||
|
void setTabModerationOpen(bool value);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void startupTabIndexChanged(int index);
|
void startupTabIndexChanged(int index);
|
||||||
|
|
|
||||||
|
|
@ -232,6 +232,12 @@ TEST_F(SettingsDefaultsTest, Tabs_AllTabsOpen_Default)
|
||||||
ASSERT_EQ(s.getTabLogOpen(), true);
|
ASSERT_EQ(s.getTabLogOpen(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(SettingsDefaultsTest, Tabs_ModerationOpen_Default)
|
||||||
|
{
|
||||||
|
TabsSettings s(settingsPath, nullptr);
|
||||||
|
ASSERT_EQ(s.getTabModerationOpen(), false);
|
||||||
|
}
|
||||||
|
|
||||||
// --- ChatSettings ---
|
// --- ChatSettings ---
|
||||||
|
|
||||||
TEST_F(SettingsDefaultsTest, Chat_Mention_Default)
|
TEST_F(SettingsDefaultsTest, Chat_Mention_Default)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue