mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-10-26 17:41:15 -05:00
[Report] Add context menu to report queue rows (#7444)
* [Report] Add report reopen action for closed reports - New Command_ReportReopen moderator command (extension 1206) with server handler that reopens resolved/dismissed reports, resets resolution state and notified, and records the reopening note in the audit log - Reopen... button in the report queue action bar, gated to resolved and dismissed reports, with optional note prompt and tooltip - Success confirmations for Assign, Resolve/Dismiss and Reopen now survive the list refresh via a pending status message instead of being clobbered - Dispatch/auth coverage in server_moderator_role_test * [Report] Surface resolution audit info in report details - Add resolved_by_name to ServerInfo_Report and join resolved_by users in the report list, my-list and details queries - Render resolution note and resolved-at/by in the report detail pane instead of duplicating the description into the chat log context * [Report] Add context menu to report queue rows - Open the standard UserContextMenu on right-click/Menu key for the reported user, selecting the row first so the details panel matches the menu target - Carry the reported user's level in ServerInfo_Report so the menu shows the same actions as the user list, with an IsUser fallback for deleted or unregistered users - Wire the Private chat action through TabReport to TabSupervisor and retranslate the menu on live language switches * [Server] Fix reported_user_level column indices in report queries The SELECT lists resolved_by_name at index 10, shifting room_id to 11, replay_id to 12, reported_user_id to 13 and reported_user_admin to 14. The code read 12/13, so 'user exists' meant 'a replay exists' and the admin flags came from the user id (e.g. id 5 -> admin+judge; users without a replay reported as unregistered, hiding registered-only actions). Fixed in cmdReportList and cmdReportMyList, plus the column comment. --------- Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
#include "tab_report.h"
|
||||
|
||||
#include "../server/user/user_context_menu.h"
|
||||
#include "../utility/report_utils.h"
|
||||
#include "abstract_client.h"
|
||||
#include "libcockatrice/protocol/pb/response.pb.h"
|
||||
@@ -33,6 +34,7 @@
|
||||
#include <QVariant>
|
||||
#include <QWidget>
|
||||
#include <QtGlobal>
|
||||
#include <libcockatrice/network/server/remote/user_level.h>
|
||||
#include <libcockatrice/protocol/pb/command_replay_download_by_game_id.pb.h>
|
||||
#include <libcockatrice/protocol/pb/command_report_add_comment.pb.h>
|
||||
#include <libcockatrice/protocol/pb/command_report_assign.pb.h>
|
||||
@@ -121,6 +123,10 @@ TabReport::TabReport(TabSupervisor *_tabSupervisor, AbstractClient *_client) : T
|
||||
table->horizontalHeader()->setSectionResizeMode(COL_REPORTED, QHeaderView::Stretch);
|
||||
table->horizontalHeader()->setSectionResizeMode(COL_REPORTER, QHeaderView::ResizeToContents);
|
||||
connect(table, &QTableWidget::itemSelectionChanged, this, &TabReport::onSelectionChanged);
|
||||
table->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(table, &QTableWidget::customContextMenuRequested, this, &TabReport::showTableContextMenu);
|
||||
userContextMenu = new UserContextMenu(tabSupervisor, this);
|
||||
connect(userContextMenu, &UserContextMenu::openMessageDialog, this, &TabReport::openMessageDialog);
|
||||
|
||||
descGroup = new QGroupBox;
|
||||
descriptionEdit = new QTextEdit;
|
||||
@@ -276,6 +282,7 @@ TabReport::TabReport(TabSupervisor *_tabSupervisor, AbstractClient *_client) : T
|
||||
|
||||
void TabReport::retranslateUi()
|
||||
{
|
||||
userContextMenu->retranslateUi();
|
||||
searchEdit->setPlaceholderText(tr("Search by username, category..."));
|
||||
statusFilter->clear();
|
||||
statusFilter->addItem(tr("All Statuses"), "");
|
||||
@@ -506,6 +513,36 @@ void TabReport::onSelectionChanged()
|
||||
updateActionStates();
|
||||
}
|
||||
|
||||
void TabReport::showTableContextMenu(const QPoint &pos)
|
||||
{
|
||||
QTableWidgetItem *clickedItem = table->itemAt(pos);
|
||||
if (!clickedItem || clickedItem->row() < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int row = clickedItem->row();
|
||||
const QTableWidgetItem *reportedItem = table->item(row, COL_REPORTED);
|
||||
const QTableWidgetItem *idItem = table->item(row, COL_ID);
|
||||
if (!reportedItem || !idItem || reportedItem->text().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int userLevel = ServerInfo_User::IsUser;
|
||||
const int reportId = idItem->data(Qt::UserRole).toInt();
|
||||
for (const ServerInfo_Report &r : filteredReports) {
|
||||
if (r.report_id() == reportId) {
|
||||
if (r.reported_user_level() != 0) {
|
||||
userLevel = r.reported_user_level();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
table->selectRow(row);
|
||||
userContextMenu->showContextMenu(table->viewport()->mapToGlobal(pos), reportedItem->text(),
|
||||
UserLevelFlags(userLevel));
|
||||
}
|
||||
|
||||
void TabReport::loadReportDetails(int reportId)
|
||||
{
|
||||
detailsRequestedReportId = reportId;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "tab.h"
|
||||
|
||||
#include <QList>
|
||||
#include <QPoint>
|
||||
#include <QString>
|
||||
#include <libcockatrice/protocol/pb/serverinfo_report.pb.h>
|
||||
#include <qtmetamacros.h>
|
||||
@@ -22,6 +23,7 @@ class QTimer;
|
||||
class GameReplay;
|
||||
class Response;
|
||||
class TabSupervisor;
|
||||
class UserContextMenu;
|
||||
|
||||
class TabReport : public Tab
|
||||
{
|
||||
@@ -37,6 +39,7 @@ public:
|
||||
signals:
|
||||
void openReplay(GameReplay *replay);
|
||||
void requestJoinGame(int gameId, int roomId);
|
||||
void openMessageDialog(const QString &userName, bool focus);
|
||||
|
||||
private slots:
|
||||
void refreshList();
|
||||
@@ -46,6 +49,7 @@ private slots:
|
||||
void resolveReport(bool dismissed, bool promptNote);
|
||||
void resolveResponse(const Response &response);
|
||||
void onSelectionChanged();
|
||||
void showTableContextMenu(const QPoint &pos);
|
||||
void viewReplay();
|
||||
void viewReplayResponse(const Response &response);
|
||||
void joinGame();
|
||||
@@ -94,6 +98,7 @@ private:
|
||||
QPushButton *joinGameButton;
|
||||
QLabel *statusLabel;
|
||||
QTimer *refreshTimer;
|
||||
UserContextMenu *userContextMenu;
|
||||
|
||||
QGroupBox *userContextGroup;
|
||||
QLabel *userContextName;
|
||||
|
||||
@@ -919,6 +919,7 @@ void TabSupervisor::openTabReport()
|
||||
myAddTab(tabReport, aTabReport);
|
||||
connect(tabReport, &TabReport::openReplay, this, &TabSupervisor::openReplay);
|
||||
connect(tabReport, &TabReport::requestJoinGame, this, &TabSupervisor::joinReportGame);
|
||||
connect(tabReport, &TabReport::openMessageDialog, this, &TabSupervisor::addMessageTab);
|
||||
connect(tabReport, &QObject::destroyed, this, [this] {
|
||||
tabReport = nullptr;
|
||||
aTabReport->setChecked(false);
|
||||
|
||||
@@ -34,4 +34,6 @@ message ServerInfo_Report {
|
||||
|
||||
optional string resolution_note = 15;
|
||||
optional string resolved_by_name = 16;
|
||||
|
||||
optional int32 reported_user_level = 17;
|
||||
}
|
||||
|
||||
@@ -1986,6 +1986,29 @@ Response::ResponseCode AbstractServerSocketInterface::cmdBanFromServer(const Com
|
||||
return Response::RespOk;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
int reportUserLevel(bool userExists, int adminFlags)
|
||||
{
|
||||
int userLevel = ServerInfo_User::IsUser;
|
||||
if (userExists) {
|
||||
userLevel |= ServerInfo_User::IsRegistered;
|
||||
if (adminFlags & 1) {
|
||||
userLevel |= ServerInfo_User::IsAdmin | ServerInfo_User::IsModerator;
|
||||
} else if (adminFlags & 2) {
|
||||
userLevel |= ServerInfo_User::IsModerator;
|
||||
}
|
||||
if (adminFlags & 4) {
|
||||
userLevel |= ServerInfo_User::IsJudge;
|
||||
}
|
||||
if (adminFlags & 8) {
|
||||
userLevel |= ServerInfo_User::IsDeveloper;
|
||||
}
|
||||
}
|
||||
return userLevel;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Response::ResponseCode AbstractServerSocketInterface::cmdReportList(const Command_ReportList &cmd,
|
||||
ResponseContainer &rc)
|
||||
{
|
||||
@@ -1999,8 +2022,9 @@ Response::ResponseCode AbstractServerSocketInterface::cmdReportList(const Comman
|
||||
|
||||
// Columns: 0=id, 1=reporter_name, 2=reported_user_name, 3=game_id,
|
||||
// 4=category, 5=description, 6=created_at, 7=status,
|
||||
// 8=resolution_note, 9=assigned_mod_name,
|
||||
// 10=room_id, 11=replay_id
|
||||
// 8=resolution_note, 9=assigned_mod_name, 10=resolved_by_name,
|
||||
// 11=room_id, 12=replay_id,
|
||||
// 13=reported_user_id, 14=reported_user_admin
|
||||
QString whereClause;
|
||||
if (unresolvedOnly) {
|
||||
whereClause = "WHERE r.status = 'open' OR r.status = 'assigned' ";
|
||||
@@ -2017,10 +2041,11 @@ Response::ResponseCode AbstractServerSocketInterface::cmdReportList(const Comman
|
||||
"r.category, r.description, r.created_at, r.status, "
|
||||
"r.resolution_note, u.name AS assigned_mod_name, ru.name AS resolved_by_name, r.room_id, "
|
||||
"(SELECT id FROM {prefix}_replays WHERE id_game = r.game_id ORDER BY id DESC LIMIT 1) "
|
||||
"AS replay_id "
|
||||
"AS replay_id, u2.id AS reported_user_id, u2.admin AS reported_user_admin "
|
||||
"FROM {prefix}_reports r "
|
||||
"LEFT JOIN {prefix}_users u ON r.assigned_to = u.id "
|
||||
"LEFT JOIN {prefix}_users ru ON r.resolved_by = ru.id ";
|
||||
"LEFT JOIN {prefix}_users ru ON r.resolved_by = ru.id "
|
||||
"LEFT JOIN {prefix}_users u2 ON u2.name = r.reported_user_name ";
|
||||
|
||||
if (unresolvedOnly) {
|
||||
queryStr += "WHERE r.status = 'open' OR r.status = 'assigned' ";
|
||||
@@ -2072,6 +2097,8 @@ Response::ResponseCode AbstractServerSocketInterface::cmdReportList(const Comman
|
||||
if (!query->value(12).isNull()) {
|
||||
info->set_replay_id(query->value(12).toInt());
|
||||
}
|
||||
|
||||
info->set_reported_user_level(reportUserLevel(!query->value(13).isNull(), query->value(14).toInt()));
|
||||
}
|
||||
|
||||
rc.setResponseExtension(re);
|
||||
@@ -2746,10 +2773,11 @@ Response::ResponseCode AbstractServerSocketInterface::cmdReportMyList(const Comm
|
||||
"r.category, r.description, r.created_at, r.status, "
|
||||
"r.resolution_note, u.name AS assigned_mod_name, ru.name AS resolved_by_name, r.room_id, "
|
||||
"(SELECT id FROM {prefix}_replays WHERE id_game = r.game_id ORDER BY id DESC LIMIT 1) "
|
||||
"AS replay_id "
|
||||
"AS replay_id, u2.id AS reported_user_id, u2.admin AS reported_user_admin "
|
||||
"FROM {prefix}_reports r "
|
||||
"LEFT JOIN {prefix}_users u ON r.assigned_to = u.id "
|
||||
"LEFT JOIN {prefix}_users ru ON r.resolved_by = ru.id "
|
||||
"LEFT JOIN {prefix}_users u2 ON u2.name = r.reported_user_name "
|
||||
"WHERE r.reporter_id = :reporter_id "
|
||||
"ORDER BY r.created_at DESC LIMIT 200";
|
||||
|
||||
@@ -2796,6 +2824,8 @@ Response::ResponseCode AbstractServerSocketInterface::cmdReportMyList(const Comm
|
||||
if (!query->value(12).isNull()) {
|
||||
info->set_replay_id(query->value(12).toInt());
|
||||
}
|
||||
|
||||
info->set_reported_user_level(reportUserLevel(!query->value(13).isNull(), query->value(14).toInt()));
|
||||
}
|
||||
|
||||
rc.setResponseExtension(re);
|
||||
|
||||
Reference in New Issue
Block a user