From 74a6acde917830c100a89ee831e4d2557673b32d Mon Sep 17 00:00:00 2001 From: BruebachL <44814898+BruebachL@users.noreply.github.com> Date: Sun, 11 Oct 2026 15:29:09 +0200 Subject: [PATCH] [Report] Surface resolution audit info in report details (#7443) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 Co-authored-by: Lukas BrĂ¼bach --- .../widgets/utility/report_utils.cpp | 21 ++++++- .../protocol/pb/serverinfo_report.proto | 1 + servatrice/src/serversocketinterface.cpp | 63 +++++++++++-------- 3 files changed, 58 insertions(+), 27 deletions(-) diff --git a/cockatrice/src/interface/widgets/utility/report_utils.cpp b/cockatrice/src/interface/widgets/utility/report_utils.cpp index a581c9837..d7fc083c6 100644 --- a/cockatrice/src/interface/widgets/utility/report_utils.cpp +++ b/cockatrice/src/interface/widgets/utility/report_utils.cpp @@ -100,10 +100,27 @@ void renderReportDetails(QTextEdit *chatLogEdit, const QString &moderatorPrefix, const QString &nonModeratorPrefix) { + QString details; + if (report.has_resolution_note() && !report.resolution_note().empty()) { + details = QObject::tr("Resolution note: %1").arg(QString::fromStdString(report.resolution_note())); + } + if (report.has_resolution_time()) { + QString resolvedBy = QObject::tr("unknown"); + if (report.has_resolved_by_name() && !report.resolved_by_name().empty()) { + resolvedBy = QString::fromStdString(report.resolved_by_name()); + } + if (!details.isEmpty()) { + details += "\n"; + } + details += QObject::tr("Resolved at %1 by %2").arg(formatReportTime(report.resolution_time()), resolvedBy); + } + if (report.has_chat_log() && !report.chat_log().empty()) { - chatLogEdit->setPlainText(QString::fromStdString(report.chat_log())); + const QString chatLog = QString::fromStdString(report.chat_log()); + chatLogEdit->setPlainText( + details.isEmpty() ? chatLog : details + "\n\n" + QObject::tr("=== Chat Log ===") + "\n" + chatLog); } else { - chatLogEdit->clear(); + chatLogEdit->setPlainText(details); } commentsEdit->clear(); diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/serverinfo_report.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/serverinfo_report.proto index a145fd855..e67b1f361 100644 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/serverinfo_report.proto +++ b/libcockatrice_protocol/libcockatrice/protocol/pb/serverinfo_report.proto @@ -33,4 +33,5 @@ message ServerInfo_Report { optional string chat_log = 14; optional string resolution_note = 15; + optional string resolved_by_name = 16; } diff --git a/servatrice/src/serversocketinterface.cpp b/servatrice/src/serversocketinterface.cpp index 8fa070ce7..177e945c2 100644 --- a/servatrice/src/serversocketinterface.cpp +++ b/servatrice/src/serversocketinterface.cpp @@ -2015,11 +2015,12 @@ Response::ResponseCode AbstractServerSocketInterface::cmdReportList(const Comman QString queryStr = "SELECT r.id, r.reporter_name, r.reported_user_name, r.game_id, " "r.category, r.description, r.created_at, r.status, " - "r.resolution_note, u.name AS assigned_mod_name, r.room_id, " + "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 " "FROM {prefix}_reports r " - "LEFT JOIN {prefix}_users u ON r.assigned_to = u.id "; + "LEFT JOIN {prefix}_users u ON r.assigned_to = u.id " + "LEFT JOIN {prefix}_users ru ON r.resolved_by = ru.id "; if (unresolvedOnly) { queryStr += "WHERE r.status = 'open' OR r.status = 'assigned' "; @@ -2060,13 +2061,16 @@ Response::ResponseCode AbstractServerSocketInterface::cmdReportList(const Comman if (!query->value(9).isNull()) { info->set_assigned_mod_name(query->value(9).toString().toStdString()); } - if (!query->value(10).isNull()) { - info->set_room_id(query->value(10).toInt()); + info->set_resolved_by_name(query->value(10).toString().toStdString()); } if (!query->value(11).isNull()) { - info->set_replay_id(query->value(11).toInt()); + info->set_room_id(query->value(11).toInt()); + } + + if (!query->value(12).isNull()) { + info->set_replay_id(query->value(12).toInt()); } } @@ -2740,11 +2744,12 @@ Response::ResponseCode AbstractServerSocketInterface::cmdReportMyList(const Comm QString queryStr = "SELECT r.id, r.reporter_name, r.reported_user_name, r.game_id, " "r.category, r.description, r.created_at, r.status, " - "r.resolution_note, u.name AS assigned_mod_name, r.room_id, " + "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 " "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 " "WHERE r.reporter_id = :reporter_id " "ORDER BY r.created_at DESC LIMIT 200"; @@ -2780,13 +2785,16 @@ Response::ResponseCode AbstractServerSocketInterface::cmdReportMyList(const Comm if (!query->value(9).isNull()) { info->set_assigned_mod_name(query->value(9).toString().toStdString()); } - if (!query->value(10).isNull()) { - info->set_room_id(query->value(10).toInt()); + info->set_resolved_by_name(query->value(10).toString().toStdString()); } if (!query->value(11).isNull()) { - info->set_replay_id(query->value(11).toInt()); + info->set_room_id(query->value(11).toInt()); + } + + if (!query->value(12).isNull()) { + info->set_replay_id(query->value(12).toInt()); } } @@ -2845,17 +2853,19 @@ Response::ResponseCode AbstractServerSocketInterface::cmdReportDetails(const Com // Columns: 0=id, 1=reporter_id, 2=reporter_name, 3=reported_user_name, 4=game_id, // 5=category, 6=description, 7=created_at, 8=status, - // 9=resolution_note, 10=assigned_mod_name, 11=chat_log, - // 12=room_id, 13=resolution_time, 14=replay_id - QString queryStr = "SELECT r.id, r.reporter_id, r.reporter_name, r.reported_user_name, r.game_id, " - "r.category, r.description, r.created_at, r.status, " - "r.resolution_note, u.name AS assigned_mod_name, r.chat_log, r.room_id, " - "r.resolution_time, " - "(SELECT id FROM {prefix}_replays WHERE id_game = r.game_id ORDER BY id DESC LIMIT 1) " - "AS replay_id " - "FROM {prefix}_reports r " - "LEFT JOIN {prefix}_users u ON r.assigned_to = u.id " - "WHERE r.id = :id"; + // 9=resolution_note, 10=assigned_mod_name, 11=resolved_by_name, + // 12=chat_log, 13=room_id, 14=resolution_time, 15=replay_id + QString queryStr = + "SELECT r.id, r.reporter_id, r.reporter_name, r.reported_user_name, r.game_id, " + "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.chat_log, r.room_id, " + "r.resolution_time, " + "(SELECT id FROM {prefix}_replays WHERE id_game = r.game_id ORDER BY id DESC LIMIT 1) " + "AS replay_id " + "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 " + "WHERE r.id = :id"; QSqlQuery *query = sqlInterface->prepareQuery(queryStr); query->bindValue(":id", reportId); @@ -2894,21 +2904,24 @@ Response::ResponseCode AbstractServerSocketInterface::cmdReportDetails(const Com if (!query->value(10).isNull()) { info->set_assigned_mod_name(query->value(10).toString().toStdString()); } - if (!query->value(11).isNull()) { - info->set_chat_log(query->value(11).toString().toStdString()); + info->set_resolved_by_name(query->value(11).toString().toStdString()); } if (!query->value(12).isNull()) { - info->set_room_id(query->value(12).toInt()); + info->set_chat_log(query->value(12).toString().toStdString()); } if (!query->value(13).isNull()) { - info->set_resolution_time(query->value(13).toDateTime().toSecsSinceEpoch()); + info->set_room_id(query->value(13).toInt()); } if (!query->value(14).isNull()) { - info->set_replay_id(query->value(14).toInt()); + info->set_resolution_time(query->value(14).toDateTime().toSecsSinceEpoch()); + } + + if (!query->value(15).isNull()) { + info->set_replay_id(query->value(15).toInt()); } QSqlQuery *commentQuery =