aboutsummaryrefslogtreecommitdiff
path: root/src/server/game/Support
diff options
context:
space:
mode:
authorCarbenium <keresztesschmidt@gmail.com>2015-02-08 01:07:00 +0100
committerCarbenium <keresztesschmidt@gmail.com>2015-03-10 21:09:21 +0100
commit37a14bc2d5066bdcdc24331c6718ccf54fb1afa5 (patch)
treea8514c64ae0e5184f20a810d36043d03ab4feac1 /src/server/game/Support
parentc8944d73b32697456b8baeaa72f98378044d3800 (diff)
Core/Support: Update the support/ticket system to 6.x
Core/Mail: fix a logic error introduced in 829e11b6623f967672143d79cd8d31772f54866d
Diffstat (limited to 'src/server/game/Support')
-rw-r--r--src/server/game/Support/SupportMgr.cpp1088
-rw-r--r--src/server/game/Support/SupportMgr.h416
2 files changed, 1504 insertions, 0 deletions
diff --git a/src/server/game/Support/SupportMgr.cpp b/src/server/game/Support/SupportMgr.cpp
new file mode 100644
index 00000000000..9183c2f7fde
--- /dev/null
+++ b/src/server/game/Support/SupportMgr.cpp
@@ -0,0 +1,1088 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ *
+ * 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 2 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 <http://www.gnu.org/licenses/>.
+ */
+#include "Chat.h"
+#include "Language.h"
+#include "SupportMgr.h"
+
+inline time_t GetAge(uint64 t) { return (time(nullptr) - t) / DAY; }
+
+Ticket::Ticket() : _id(0), _mapId(0), _createTime(0) { }
+
+Ticket::Ticket(Player* player) : _id(0), _mapId(0), _createTime(time(nullptr))
+{
+ _playerGuid = player->GetGUID();
+}
+
+Ticket::~Ticket() { }
+
+void Ticket::TeleportTo(Player* player) const
+{
+ player->TeleportTo(_mapId, _pos.x, _pos.y, _pos.z, 0.0f, 0);
+}
+
+std::string Ticket::FormatViewMessageString(ChatHandler& handler, const char* szClosedName, const char* szAssignedToName, const char* szUnassignedName, const char* szDeletedName, const char* szCompletedName) const
+{
+ std::stringstream ss;
+ ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTGUID, _id);
+ ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTNAME, GetPlayer()->GetName().c_str());
+ if (szClosedName)
+ ss << handler.PGetParseString(LANG_COMMAND_TICKETCLOSED, szClosedName);
+ if (szAssignedToName)
+ ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTASSIGNEDTO, szAssignedToName);
+ if (szUnassignedName)
+ ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTUNASSIGNED, szUnassignedName);
+ if (szDeletedName)
+ ss << handler.PGetParseString(LANG_COMMAND_TICKETDELETED, szDeletedName);
+ return ss.str();
+}
+
+GmTicket::GmTicket() : _lastModifiedTime(0), _completed(false), _escalatedStatus(TICKET_UNASSIGNED), _viewed(false), _needResponse(false), _needMoreHelp(false) { }
+
+GmTicket::GmTicket(Player* player) : Ticket(player), _lastModifiedTime(time(nullptr)), _completed(false), _escalatedStatus(TICKET_UNASSIGNED),
+_viewed(false), _needResponse(false), _needMoreHelp(false)
+{
+ _id = sSupportMgr->GenerateGmTicketId();
+}
+
+GmTicket::~GmTicket() { }
+
+void GmTicket::SetGmAction(uint32 needResponse, bool needMoreHelp)
+{
+ _needResponse = (needResponse == 17); // Requires GM response. 17 = true, 1 = false (17 is default)
+ _needMoreHelp = needMoreHelp; // Requests further GM interaction on a ticket to which a GM has already responded. Basically means "has a new ticket"
+}
+
+void GmTicket::SetUnassigned()
+{
+ _assignedTo.Clear();
+ switch (_escalatedStatus)
+ {
+ case TICKET_ASSIGNED: _escalatedStatus = TICKET_UNASSIGNED;
+ break;
+ case TICKET_ESCALATED_ASSIGNED: _escalatedStatus = TICKET_IN_ESCALATION_QUEUE;
+ break;
+ case TICKET_UNASSIGNED:
+ case TICKET_IN_ESCALATION_QUEUE:
+ default:
+ break;
+ }
+}
+
+void GmTicket::SetChatLog(std::list<uint32> time, std::string const& log)
+{
+ std::stringstream ss(log);
+ std::stringstream newss;
+ std::string line;
+ while (std::getline(ss, line) && !time.empty())
+ {
+ newss << secsToTimeString(time.front()) << ": " << line << "\n";
+ time.pop_front();
+ }
+
+ _chatLog = newss.str();
+}
+
+void GmTicket::SendResponse(WorldSession* session) const
+{
+ WorldPackets::Ticket::GMTicketResponse resp;
+ resp.TicketID = GetId();
+ resp.ResponseID = 2; //TODO : research
+ resp.Description = GetDescription();
+ resp.ResponseText = GetResponse();
+
+ session->SendPacket(resp.Write());
+}
+
+void GmTicket::LoadFromDB(Field* fields)
+{
+ uint8 idx = 0;
+ _id = fields[ idx].GetUInt32();
+ _playerGuid = ObjectGuid::Create<HighGuid::Player>(fields[++idx].GetUInt64());
+ _description = fields[++idx].GetString();
+ _createTime = fields[++idx].GetUInt32();
+ _mapId = fields[++idx].GetUInt16();
+ _pos.x = fields[++idx].GetFloat();
+ _pos.y = fields[++idx].GetFloat();
+ _pos.z = fields[++idx].GetFloat();
+ _lastModifiedTime = fields[++idx].GetUInt32();
+
+ int64 closedBy = fields[++idx].GetInt64();
+ if (closedBy == 0)
+ _closedBy = ObjectGuid::Empty;
+ else if (closedBy < 0)
+ _closedBy.SetRawValue(0, uint64(closedBy));
+ else
+ _closedBy = ObjectGuid::Create<HighGuid::Player>(uint64(closedBy));
+
+ uint64 assignedTo = fields[++idx].GetUInt64();
+ if (assignedTo == 0)
+ _assignedTo = ObjectGuid::Empty;
+ else
+ _assignedTo = ObjectGuid::Create<HighGuid::Player>(assignedTo);
+
+ _comment = fields[++idx].GetString();
+ _response = fields[++idx].GetString();
+ _completed = fields[++idx].GetBool();
+ _escalatedStatus = GMTicketEscalationStatus(fields[++idx].GetUInt8());
+ _viewed = fields[++idx].GetBool();
+ _needMoreHelp = fields[++idx].GetBool();
+}
+
+void GmTicket::SaveToDB(SQLTransaction& trans) const
+{
+ uint8 idx = 0;
+ PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_GM_TICKET);
+ stmt->setUInt32(idx, _id);
+ stmt->setUInt64(++idx, _playerGuid.GetCounter());
+ stmt->setString(++idx, _description);
+ stmt->setUInt32(++idx, _createTime);
+ stmt->setUInt16(++idx, _mapId);
+ stmt->setFloat(++idx, _pos.x);
+ stmt->setFloat(++idx, _pos.y);
+ stmt->setFloat(++idx, _pos.z);
+ stmt->setUInt32(++idx, uint32(_lastModifiedTime));
+ stmt->setInt64(++idx, int64(_closedBy.GetCounter()));
+ stmt->setUInt64(++idx, _assignedTo.GetCounter());
+ stmt->setString(++idx, _comment);
+ stmt->setString(++idx, _response);
+ stmt->setBool(++idx, _completed);
+ stmt->setUInt8(++idx, uint8(_escalatedStatus));
+ stmt->setBool(++idx, _viewed);
+ stmt->setBool(++idx, _needMoreHelp);
+
+ CharacterDatabase.ExecuteOrAppend(trans, stmt);
+}
+
+void GmTicket::DeleteFromDB()
+{
+ PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GM_TICKET);
+ stmt->setUInt32(0, _id);
+ CharacterDatabase.Execute(stmt);
+}
+
+std::string GmTicket::FormatViewMessageString(ChatHandler& handler, bool detailed) const
+{
+ time_t curTime = time(nullptr);
+
+ std::stringstream ss;
+ ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTGUID, _id);
+ ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTNAME, GetPlayer()->GetName().c_str());
+ ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTAGECREATE, (secsToTimeString(curTime - _createTime, true, false)).c_str());
+ ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTAGE, (secsToTimeString(curTime - _lastModifiedTime, true, false)).c_str());
+
+ std::string name;
+ if (ObjectMgr::GetPlayerNameByGUID(_assignedTo, name))
+ ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTASSIGNEDTO, name.c_str());
+
+ if (detailed)
+ {
+ ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTMESSAGE, _description.c_str());
+ if (!_comment.empty())
+ ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTCOMMENT, _comment.c_str());
+ if (!_response.empty())
+ ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTRESPONSE, _response.c_str());
+ }
+ return ss.str();
+}
+
+std::string GmTicket::FormatViewMessageString(ChatHandler& handler, const char* szClosedName, const char* szAssignedToName, const char* szUnassignedName, const char* szDeletedName, const char* szCompletedName) const
+{
+ std::stringstream ss;
+ ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTGUID, _id);
+ ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTNAME, GetPlayer()->GetName().c_str());
+ if (szClosedName)
+ ss << handler.PGetParseString(LANG_COMMAND_TICKETCLOSED, szClosedName);
+ if (szAssignedToName)
+ ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTASSIGNEDTO, szAssignedToName);
+ if (szUnassignedName)
+ ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTUNASSIGNED, szUnassignedName);
+ if (szDeletedName)
+ ss << handler.PGetParseString(LANG_COMMAND_TICKETDELETED, szDeletedName);
+ if (szCompletedName)
+ ss << handler.PGetParseString(LANG_COMMAND_TICKETCOMPLETED, szCompletedName);
+ return ss.str();
+}
+
+BugTicket::BugTicket() : _facing(0.0f) { }
+
+BugTicket::BugTicket(Player* player) : Ticket(player)
+{
+ _id = sSupportMgr->GenerateBugId();
+}
+
+BugTicket::~BugTicket() { }
+
+void BugTicket::LoadFromDB(Field* fields)
+{
+ uint8 idx = 0;
+ _id = fields[ idx].GetUInt32();
+ _playerGuid = ObjectGuid::Create<HighGuid::Player>(fields[++idx].GetUInt64());
+ _note = fields[++idx].GetString();
+ _createTime = fields[++idx].GetUInt32();
+ _mapId = fields[++idx].GetUInt32();
+ _pos.x = fields[++idx].GetFloat();
+ _pos.y = fields[++idx].GetFloat();
+ _pos.z = fields[++idx].GetFloat();
+ _facing = fields[++idx].GetFloat();
+
+ int64 closedBy = fields[++idx].GetInt64();
+ if (closedBy == 0)
+ _closedBy = ObjectGuid::Empty;
+ else if (closedBy < 0)
+ _closedBy.SetRawValue(0, uint64(closedBy));
+ else
+ _closedBy = ObjectGuid::Create<HighGuid::Player>(uint64(closedBy));
+
+ uint64 assignedTo = fields[++idx].GetUInt64();
+ if (assignedTo == 0)
+ _assignedTo = ObjectGuid::Empty;
+ else
+ _assignedTo = ObjectGuid::Create<HighGuid::Player>(assignedTo);
+
+ _comment = fields[++idx].GetString();
+}
+
+void BugTicket::SaveToDB(SQLTransaction& trans) const
+{
+ uint8 idx = 0;
+ PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_GM_BUG);
+ stmt->setUInt32(idx, _id);
+ stmt->setUInt64(++idx, _playerGuid.GetCounter());
+ stmt->setString(++idx, _note);
+ stmt->setUInt32(++idx, _mapId);
+ stmt->setFloat(++idx, _pos.x);
+ stmt->setFloat(++idx, _pos.y);
+ stmt->setFloat(++idx, _pos.z);
+ stmt->setFloat(++idx, _facing);
+ stmt->setInt64(++idx, _closedBy.GetCounter());
+ stmt->setUInt64(++idx, _assignedTo.GetCounter());
+ stmt->setString(++idx, _comment);
+
+ CharacterDatabase.ExecuteOrAppend(trans, stmt);
+}
+
+void BugTicket::DeleteFromDB()
+{
+ PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GM_BUG);
+ stmt->setUInt32(0, _id);
+ CharacterDatabase.Execute(stmt);
+}
+
+std::string BugTicket::FormatViewMessageString(ChatHandler& handler, bool detailed) const
+{
+ time_t curTime = time(nullptr);
+
+ std::stringstream ss;
+ ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTGUID, _id);
+ ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTNAME, GetPlayer()->GetName().c_str());
+ ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTAGECREATE, (secsToTimeString(curTime - _createTime, true, false)).c_str());
+
+ std::string name;
+ if (ObjectMgr::GetPlayerNameByGUID(_assignedTo, name))
+ ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTASSIGNEDTO, name.c_str());
+
+ if (detailed)
+ {
+ ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTMESSAGE, _note.c_str());
+ if (!_comment.empty())
+ ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTCOMMENT, _comment.c_str());
+ }
+ return ss.str();
+}
+
+ComplaintTicket::ComplaintTicket() : _facing(0.0f), _complaintType(GMTICKET_SUPPORT_COMPLAINT_TYPE_NONE) { }
+
+ComplaintTicket::ComplaintTicket(Player* player) : Ticket(player), _facing(0.0f), _complaintType(GMTICKET_SUPPORT_COMPLAINT_TYPE_NONE)
+{
+ _id = sSupportMgr->GenerateComplaintId();
+}
+
+ComplaintTicket::~ComplaintTicket() { }
+
+void ComplaintTicket::LoadFromDB(Field* fields)
+{
+ uint8 idx = 0;
+ _id = fields[ idx].GetUInt32();
+ _playerGuid = ObjectGuid::Create<HighGuid::Player>(fields[++idx].GetUInt64());
+ _note = fields[++idx].GetString();
+ _createTime = fields[++idx].GetUInt32();
+ _mapId = fields[++idx].GetUInt32();
+ _pos.x = fields[++idx].GetFloat();
+ _pos.y = fields[++idx].GetFloat();
+ _pos.z = fields[++idx].GetFloat();
+ _facing = fields[++idx].GetFloat();
+ _targetCharacterGuid = ObjectGuid::Create<HighGuid::Player>(fields[++idx].GetUInt64());
+ _complaintType = GMSupportComplaintType(fields[++idx].GetUInt8());
+ int32 reportLineIndex = fields[++idx].GetInt32();
+ if (reportLineIndex != -1)
+ _chatLog.ReportLineIndex.Set(reportLineIndex);
+
+ int64 closedBy = fields[++idx].GetInt64();
+ if (closedBy == 0)
+ _closedBy = ObjectGuid::Empty;
+ else if (closedBy < 0)
+ _closedBy.SetRawValue(0, uint64(closedBy));
+ else
+ _closedBy = ObjectGuid::Create<HighGuid::Player>(uint64(closedBy));
+
+ uint64 assignedTo = fields[++idx].GetUInt64();
+ if (assignedTo == 0)
+ _assignedTo = ObjectGuid::Empty;
+ else
+ _assignedTo = ObjectGuid::Create<HighGuid::Player>(assignedTo);
+
+ _comment = fields[++idx].GetString();
+}
+
+void ComplaintTicket::LoadChatLineFromDB(Field* fields)
+{
+ _chatLog.Lines.emplace_back(fields[0].GetUInt32(), fields[1].GetString());
+}
+
+void ComplaintTicket::SaveToDB(SQLTransaction& trans) const
+{
+ bool isInTransaction = bool(trans);
+ if (!isInTransaction)
+ trans = CharacterDatabase.BeginTransaction();
+
+ uint8 idx = 0;
+ PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_GM_COMPLAINT);
+ stmt->setUInt32(idx, _id);
+ stmt->setUInt64(++idx, _playerGuid.GetCounter());
+ stmt->setString(++idx, _note);
+ stmt->setUInt32(++idx, _mapId);
+ stmt->setFloat(++idx, _pos.x);
+ stmt->setFloat(++idx, _pos.y);
+ stmt->setFloat(++idx, _pos.z);
+ stmt->setFloat(++idx, _facing);
+ stmt->setUInt64(++idx, _targetCharacterGuid.GetCounter());
+ stmt->setUInt8(++idx, _complaintType);
+ if (_chatLog.ReportLineIndex.HasValue)
+ stmt->setInt32(++idx, _chatLog.ReportLineIndex.Value);
+ else
+ stmt->setInt32(++idx, -1); // empty ReportLineIndex
+ stmt->setInt64(++idx, _closedBy.GetCounter());
+ stmt->setUInt64(++idx, _assignedTo.GetCounter());
+ stmt->setString(++idx, _comment);
+ trans->Append(stmt);
+
+ uint32 lineIndex = 0;
+ for (auto const& c : _chatLog.Lines)
+ {
+ idx = 0;
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GM_COMPLAINT_CHATLINE);
+ stmt->setUInt32(idx, _id);
+ stmt->setUInt32(++idx, lineIndex);
+ stmt->setUInt32(++idx, c.Timestamp);
+ stmt->setString(++idx, c.Text);
+
+ trans->Append(stmt);
+ ++lineIndex;
+ }
+
+ if (!isInTransaction)
+ CharacterDatabase.CommitTransaction(trans);
+}
+
+void ComplaintTicket::DeleteFromDB()
+{
+ PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GM_COMPLAINT);
+ stmt->setUInt32(0, _id);
+ CharacterDatabase.Execute(stmt);
+
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GM_COMPLAINT_CHATLOG);
+ stmt->setUInt32(0, _id);
+}
+
+std::string ComplaintTicket::FormatViewMessageString(ChatHandler& handler, bool detailed) const
+{
+ time_t curTime = time(nullptr);
+
+ std::stringstream ss;
+ ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTGUID, _id);
+ ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTNAME, GetPlayer()->GetName().c_str());
+ ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTAGECREATE, (secsToTimeString(curTime - _createTime, true, false)).c_str());
+
+ std::string name;
+ if (ObjectMgr::GetPlayerNameByGUID(_assignedTo, name))
+ ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTASSIGNEDTO, name.c_str());
+
+ if (detailed)
+ {
+ ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTMESSAGE, _note.c_str());
+ if (!_comment.empty())
+ ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTCOMMENT, _comment.c_str());
+ }
+ return ss.str();
+}
+
+SuggestionTicket::SuggestionTicket() : _facing(0.0f) { }
+
+SuggestionTicket::SuggestionTicket(Player* player) : Ticket(player)
+{
+ _id = sSupportMgr->GenerateSuggestionId();
+}
+
+SuggestionTicket::~SuggestionTicket() { }
+
+void SuggestionTicket::LoadFromDB(Field* fields)
+{
+ uint8 idx = 0;
+ _id = fields[ idx].GetUInt32();
+ _playerGuid = ObjectGuid::Create<HighGuid::Player>(fields[++idx].GetUInt64());
+ _note = fields[++idx].GetString();
+ _createTime = fields[++idx].GetUInt32();
+ _mapId = fields[++idx].GetUInt32();
+ _pos.x = fields[++idx].GetFloat();
+ _pos.y = fields[++idx].GetFloat();
+ _pos.z = fields[++idx].GetFloat();
+ _facing = fields[++idx].GetFloat();
+
+ int64 closedBy = fields[++idx].GetInt64();
+ if (closedBy == 0)
+ _closedBy = ObjectGuid::Empty;
+ else if (closedBy < 0)
+ _closedBy.SetRawValue(0, uint64(closedBy));
+ else
+ _closedBy = ObjectGuid::Create<HighGuid::Player>(uint64(closedBy));
+
+ uint64 assignedTo = fields[++idx].GetUInt64();
+ if (assignedTo == 0)
+ _assignedTo = ObjectGuid::Empty;
+ else
+ _assignedTo = ObjectGuid::Create<HighGuid::Player>(assignedTo);
+
+ _comment = fields[++idx].GetString();
+}
+
+void SuggestionTicket::SaveToDB(SQLTransaction& trans) const
+{
+ uint8 idx = 0;
+ PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_GM_SUGGESTION);
+ stmt->setUInt32(idx, _id);
+ stmt->setUInt64(++idx, _playerGuid.GetCounter());
+ stmt->setString(++idx, _note);
+ stmt->setUInt32(++idx, _mapId);
+ stmt->setFloat(++idx, _pos.x);
+ stmt->setFloat(++idx, _pos.y);
+ stmt->setFloat(++idx, _pos.z);
+ stmt->setFloat(++idx, _facing);
+ stmt->setInt64(++idx, _closedBy.GetCounter());
+ stmt->setUInt64(++idx, _assignedTo.GetCounter());
+ stmt->setString(++idx, _comment);
+
+ CharacterDatabase.ExecuteOrAppend(trans, stmt);
+}
+
+void SuggestionTicket::DeleteFromDB()
+{
+ PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GM_SUGGESTION);
+ stmt->setUInt32(0, _id);
+ CharacterDatabase.Execute(stmt);
+}
+
+std::string SuggestionTicket::FormatViewMessageString(ChatHandler& handler, bool detailed) const
+{
+ time_t curTime = time(nullptr);
+
+ std::stringstream ss;
+ ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTGUID, _id);
+ ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTNAME, GetPlayer()->GetName().c_str());
+ ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTAGECREATE, (secsToTimeString(curTime - _createTime, true, false)).c_str());
+
+ std::string name;
+ if (ObjectMgr::GetPlayerNameByGUID(_assignedTo, name))
+ ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTASSIGNEDTO, name.c_str());
+
+ if (detailed)
+ {
+ ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTMESSAGE, _note.c_str());
+ if (!_comment.empty())
+ ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTCOMMENT, _comment.c_str());
+ }
+ return ss.str();
+}
+
+SupportMgr::SupportMgr() : _lastGmTicketId(0), _lastBugId(0), _lastComplaintId(0), _lastSuggestionId(0), _openGmTicketCount(0),
+_openBugTicketCount(0), _openComplaintTicketCount(0), _openSuggestionTicketCount(0) { }
+
+SupportMgr::~SupportMgr()
+{
+ for (auto const& t : _gmTicketList)
+ delete t.second;
+
+ for (auto const& b : _bugTicketList)
+ delete b.second;
+
+ for (auto const& c : _complaintTicketList)
+ delete c.second;
+
+ for (auto const& s : _suggestionTicketList)
+ delete s.second;
+}
+
+void SupportMgr::Initialize()
+{
+ SetSupportSystemStatus(sWorld->getBoolConfig(CONFIG_SUPPORT_ENABLED));
+ SetTicketSystemStatus(sWorld->getBoolConfig(CONFIG_SUPPORT_TICKETS_ENABLED));
+ SetBugSystemStatus(sWorld->getBoolConfig(CONFIG_SUPPORT_BUGS_ENABLED));
+ SetComplaintSystemStatus(sWorld->getBoolConfig(CONFIG_SUPPORT_COMPLAINTS_ENABLED));
+ SetSuggestionSystemStatus(sWorld->getBoolConfig(CONFIG_SUPPORT_SUGGESTIONS_ENABLED));
+}
+
+template<>
+GmTicket* SupportMgr::GetTicket<GmTicket>(uint32 ticketId)
+{
+ GmTicketList::const_iterator itr = _gmTicketList.find(ticketId);
+ if (itr != _gmTicketList.end())
+ return itr->second;
+
+ return nullptr;
+
+}
+
+template<>
+BugTicket* SupportMgr::GetTicket<BugTicket>(uint32 bugId)
+{
+ BugTicketList::const_iterator itr = _bugTicketList.find(bugId);
+ if (itr != _bugTicketList.end())
+ return itr->second;
+
+ return nullptr;
+
+}
+
+template<>
+ComplaintTicket* SupportMgr::GetTicket<ComplaintTicket>(uint32 complaintId)
+{
+ ComplaintTicketList::const_iterator itr = _complaintTicketList.find(complaintId);
+ if (itr != _complaintTicketList.end())
+ return itr->second;
+
+ return nullptr;
+}
+
+template<>
+SuggestionTicket* SupportMgr::GetTicket<SuggestionTicket>(uint32 suggestionId)
+{
+ SuggestionTicketList::const_iterator itr = _suggestionTicketList.find(suggestionId);
+ if (itr != _suggestionTicketList.end())
+ return itr->second;
+
+ return nullptr;
+
+}
+
+template<>
+uint32 SupportMgr::GetOpenTicketCount<GmTicket>() const { return _openGmTicketCount; }
+
+template<>
+uint32 SupportMgr::GetOpenTicketCount<BugTicket>() const { return _openBugTicketCount; }
+
+template<>
+uint32 SupportMgr::GetOpenTicketCount<ComplaintTicket>() const { return _openComplaintTicketCount; }
+
+template<>
+uint32 SupportMgr::GetOpenTicketCount<SuggestionTicket>() const { return _openSuggestionTicketCount; }
+
+void SupportMgr::LoadGmTickets()
+{
+ uint32 oldMSTime = getMSTime();
+
+ for (auto const& c : _gmTicketList)
+ delete c.second;
+ _gmTicketList.clear();
+
+ _lastGmTicketId = 0;
+ _openGmTicketCount = 0;
+
+ PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_GM_TICKETS);
+ PreparedQueryResult result = CharacterDatabase.Query(stmt);
+ if (!result)
+ {
+ TC_LOG_INFO("server.loading", ">> Loaded 0 GM tickets. DB table `gm_ticket` is empty!");
+ return;
+ }
+
+ uint32 count = 0;
+ do
+ {
+ Field* fields = result->Fetch();
+ GmTicket* ticket = new GmTicket();
+ ticket->LoadFromDB(fields);
+
+ if (!ticket->IsClosed())
+ ++_openGmTicketCount;
+
+ uint32 id = ticket->GetId();
+ if (_lastGmTicketId < id)
+ _lastGmTicketId = id;
+
+ _gmTicketList[id] = ticket;
+ ++count;
+ } while (result->NextRow());
+
+ TC_LOG_INFO("server.loading", ">> Loaded %u GM tickets in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
+}
+
+void SupportMgr::LoadBugTickets()
+{
+ uint32 oldMSTime = getMSTime();
+
+ for (auto const& c : _bugTicketList)
+ delete c.second;
+ _bugTicketList.clear();
+
+ _lastBugId = 0;
+ _openBugTicketCount = 0;
+
+ PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_GM_BUGS);
+ PreparedQueryResult result = CharacterDatabase.Query(stmt);
+ if (!result)
+ {
+ TC_LOG_INFO("server.loading", ">> Loaded 0 GM bugs. DB table `gm_bug` is empty!");
+ return;
+ }
+
+ uint32 count = 0;
+ do
+ {
+ Field* fields = result->Fetch();
+ BugTicket* bug = new BugTicket();
+ bug->LoadFromDB(fields);
+
+ if (!bug->IsClosed())
+ ++_openBugTicketCount;
+
+ uint32 id = bug->GetId();
+ if (_lastBugId < id)
+ _lastBugId = id;
+
+ _bugTicketList[id] = bug;
+ ++count;
+ } while (result->NextRow());
+
+ TC_LOG_INFO("server.loading", ">> Loaded %u GM bugs in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
+}
+
+void SupportMgr::LoadComplaintTickets()
+{
+ uint32 oldMSTime = getMSTime();
+
+ for (auto const& c : _complaintTicketList)
+ delete c.second;
+ _complaintTicketList.clear();
+
+ _lastComplaintId = 0;
+ _openComplaintTicketCount = 0;
+
+ PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_GM_COMPLAINTS);
+ PreparedQueryResult result = CharacterDatabase.Query(stmt);
+ if (!result)
+ {
+ TC_LOG_INFO("server.loading", ">> Loaded 0 GM complaints. DB table `gm_complaint` is empty!");
+ return;
+ }
+
+ uint32 count = 0;
+ PreparedStatement* chatLogStmt;
+ PreparedQueryResult chatLogResult;
+ do
+ {
+ Field* fields = result->Fetch();
+ ComplaintTicket* complaint = new ComplaintTicket();
+ complaint->LoadFromDB(fields);
+
+ if (!complaint->IsClosed())
+ ++_openComplaintTicketCount;
+
+ uint32 id = complaint->GetId();
+ if (_lastComplaintId < id)
+ _lastComplaintId = id;
+
+ chatLogStmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_GM_COMPLAINT_CHATLINES);
+ chatLogResult = CharacterDatabase.Query(stmt);
+
+ if (chatLogResult)
+ {
+ do
+ {
+ Field* chatLineFields = chatLogResult->Fetch();
+ complaint->LoadChatLineFromDB(chatLineFields);
+ } while (chatLogResult->NextRow());
+ }
+
+ _complaintTicketList[id] = complaint;
+ ++count;
+ } while (result->NextRow());
+
+ TC_LOG_INFO("server.loading", ">> Loaded %u GM complaints in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
+}
+
+void SupportMgr::LoadSuggestionTickets()
+{
+ uint32 oldMSTime = getMSTime();
+
+ for (auto const& c : _suggestionTicketList)
+ delete c.second;
+ _suggestionTicketList.clear();
+
+ _lastSuggestionId = 0;
+ _openSuggestionTicketCount = 0;
+
+ PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_GM_SUGGESTIONS);
+ PreparedQueryResult result = CharacterDatabase.Query(stmt);
+ if (!result)
+ {
+ TC_LOG_INFO("server.loading", ">> Loaded 0 GM suggestions. DB table `gm_suggestion` is empty!");
+ return;
+ }
+
+ uint32 count = 0;
+ do
+ {
+ Field* fields = result->Fetch();
+ SuggestionTicket* suggestion = new SuggestionTicket();
+ suggestion->LoadFromDB(fields);
+
+ if (!suggestion->IsClosed())
+ ++_openSuggestionTicketCount;
+
+ uint32 id = suggestion->GetId();
+ if (_lastSuggestionId < id)
+ _lastSuggestionId = id;
+
+ _suggestionTicketList[id] = suggestion;
+ ++count;
+ } while (result->NextRow());
+
+ TC_LOG_INFO("server.loading", ">> Loaded %u GM suggestions in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
+}
+
+void SupportMgr::AddTicket(GmTicket* ticket)
+{
+ _gmTicketList[ticket->GetId()] = ticket;
+ if (!ticket->IsClosed())
+ ++_openGmTicketCount;
+
+ SQLTransaction trans = SQLTransaction(nullptr);
+ ticket->SaveToDB(trans);
+}
+
+void SupportMgr::AddTicket(BugTicket* ticket)
+{
+ _bugTicketList[ticket->GetId()] = ticket;
+ if (!ticket->IsClosed())
+ ++_openBugTicketCount;
+
+ SQLTransaction trans = SQLTransaction(nullptr);
+ ticket->SaveToDB(trans);
+}
+
+void SupportMgr::AddTicket(ComplaintTicket* ticket)
+{
+ _complaintTicketList[ticket->GetId()] = ticket;
+ if (!ticket->IsClosed())
+ ++_openComplaintTicketCount;
+
+ SQLTransaction trans = SQLTransaction(nullptr);
+ ticket->SaveToDB(trans);
+}
+
+void SupportMgr::AddTicket(SuggestionTicket* ticket)
+{
+ _suggestionTicketList[ticket->GetId()] = ticket;
+ if (!ticket->IsClosed())
+ ++_openSuggestionTicketCount;
+
+ SQLTransaction trans = SQLTransaction(nullptr);
+ ticket->SaveToDB(trans);
+}
+
+template<>
+void SupportMgr::RemoveTicket<GmTicket>(uint32 ticketId)
+{
+ if (GmTicket* ticket = GetTicket<GmTicket>(ticketId))
+ {
+ ticket->DeleteFromDB();
+ _gmTicketList.erase(ticketId);
+ delete ticket;
+ }
+}
+
+template<>
+void SupportMgr::RemoveTicket<BugTicket>(uint32 ticketId)
+{
+ if (BugTicket* ticket = GetTicket<BugTicket>(ticketId))
+ {
+ ticket->DeleteFromDB();
+ _bugTicketList.erase(ticketId);
+ delete ticket;
+ }
+}
+
+template<>
+void SupportMgr::RemoveTicket<ComplaintTicket>(uint32 ticketId)
+{
+ if (ComplaintTicket* ticket = GetTicket<ComplaintTicket>(ticketId))
+ {
+ ticket->DeleteFromDB();
+ _complaintTicketList.erase(ticketId);
+ delete ticket;
+ }
+}
+
+template<>
+void SupportMgr::RemoveTicket<SuggestionTicket>(uint32 ticketId)
+{
+ if (SuggestionTicket* ticket = GetTicket<SuggestionTicket>(ticketId))
+ {
+ ticket->DeleteFromDB();
+ _suggestionTicketList.erase(ticketId);
+ delete ticket;
+ }
+}
+
+template<>
+void SupportMgr::CloseTicket<GmTicket>(uint32 ticketId, ObjectGuid closedBy)
+{
+ if (GmTicket* ticket = GetTicket<GmTicket>(ticketId))
+ {
+ SQLTransaction trans = SQLTransaction(NULL);
+ ticket->SetClosedBy(closedBy);
+ if (!closedBy.IsEmpty())
+ --_openGmTicketCount;
+ ticket->SaveToDB(trans);
+ }
+}
+
+template<>
+void SupportMgr::CloseTicket<BugTicket>(uint32 ticketId, ObjectGuid closedBy)
+{
+ if (BugTicket* ticket = GetTicket<BugTicket>(ticketId))
+ {
+ SQLTransaction trans = SQLTransaction(NULL);
+ ticket->SetClosedBy(closedBy);
+ if (!closedBy.IsEmpty())
+ --_openBugTicketCount;
+ ticket->SaveToDB(trans);
+ }
+}
+
+template<>
+void SupportMgr::CloseTicket<ComplaintTicket>(uint32 ticketId, ObjectGuid closedBy)
+{
+ if (ComplaintTicket* ticket = GetTicket<ComplaintTicket>(ticketId))
+ {
+ SQLTransaction trans = SQLTransaction(NULL);
+ ticket->SetClosedBy(closedBy);
+ if (!closedBy.IsEmpty())
+ --_openComplaintTicketCount;
+ ticket->SaveToDB(trans);
+ }
+}
+
+template<>
+void SupportMgr::CloseTicket<SuggestionTicket>(uint32 ticketId, ObjectGuid closedBy)
+{
+ if (SuggestionTicket* ticket = GetTicket<SuggestionTicket>(ticketId))
+ {
+ SQLTransaction trans = SQLTransaction(NULL);
+ ticket->SetClosedBy(closedBy);
+ if (!closedBy.IsEmpty())
+ --_openSuggestionTicketCount;
+ ticket->SaveToDB(trans);
+ }
+}
+
+template<>
+void SupportMgr::ResetTickets<GmTicket>()
+{
+ for (auto const& c : _gmTicketList)
+ delete c.second;
+ _gmTicketList.clear();
+
+ _lastGmTicketId = 0;
+
+ PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ALL_GM_TICKETS);
+ CharacterDatabase.Execute(stmt);
+}
+
+template<>
+void SupportMgr::ResetTickets<BugTicket>()
+{
+ for (auto const& c : _bugTicketList)
+ delete c.second;
+ _bugTicketList.clear();
+
+ _lastBugId = 0;
+
+ PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ALL_GM_BUGS);
+ CharacterDatabase.Execute(stmt);
+}
+
+template<>
+void SupportMgr::ResetTickets<ComplaintTicket>()
+{
+ for (auto const& c : _complaintTicketList)
+ delete c.second;
+ _complaintTicketList.clear();
+
+ _lastComplaintId = 0;
+
+ SQLTransaction trans = CharacterDatabase.BeginTransaction();
+ trans->Append(CharacterDatabase.GetPreparedStatement(CHAR_DEL_ALL_GM_COMPLAINTS));
+ trans->Append(CharacterDatabase.GetPreparedStatement(CHAR_DEL_ALL_GM_COMPLAINT_CHATLOGS));
+ CharacterDatabase.CommitTransaction(trans);
+}
+
+template<>
+void SupportMgr::ResetTickets<SuggestionTicket>()
+{
+ for (auto const& c : _suggestionTicketList)
+ delete c.second;
+ _suggestionTicketList.clear();
+
+ _lastSuggestionId = 0;
+
+ PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ALL_GM_SUGGESTIONS);
+ CharacterDatabase.Execute(stmt);
+}
+
+template<>
+void SupportMgr::ShowList<GmTicket>(ChatHandler& handler, bool onlineOnly) const
+{
+ handler.SendSysMessage(onlineOnly ? LANG_COMMAND_TICKETSHOWONLINELIST : LANG_COMMAND_TICKETSHOWLIST);
+ for (GmTicketList::const_iterator itr = _gmTicketList.begin(); itr != _gmTicketList.end(); ++itr)
+ if (!itr->second->IsClosed() && !itr->second->IsCompleted())
+ if (!onlineOnly || itr->second->GetPlayer())
+ handler.SendSysMessage(itr->second->FormatViewMessageString(handler).c_str());
+}
+
+template<>
+void SupportMgr::ShowList<GmTicket>(ChatHandler& handler) const
+{
+ handler.SendSysMessage(LANG_COMMAND_TICKETSHOWLIST);
+ for (GmTicketList::const_iterator itr = _gmTicketList.begin(); itr != _gmTicketList.end(); ++itr)
+ if (!itr->second->IsClosed() && !itr->second->IsCompleted())
+ handler.SendSysMessage(itr->second->FormatViewMessageString(handler).c_str());
+}
+
+template<>
+void SupportMgr::ShowList<BugTicket>(ChatHandler& handler) const
+{
+ handler.SendSysMessage(LANG_COMMAND_TICKETSHOWLIST);
+ for (BugTicketList::const_iterator itr = _bugTicketList.begin(); itr != _bugTicketList.end(); ++itr)
+ if (!itr->second->IsClosed())
+ handler.SendSysMessage(itr->second->FormatViewMessageString(handler).c_str());
+}
+
+template<>
+void SupportMgr::ShowList<ComplaintTicket>(ChatHandler& handler) const
+{
+ handler.SendSysMessage(LANG_COMMAND_TICKETSHOWLIST);
+ for (ComplaintTicketList::const_iterator itr = _complaintTicketList.begin(); itr != _complaintTicketList.end(); ++itr)
+ if (!itr->second->IsClosed())
+ handler.SendSysMessage(itr->second->FormatViewMessageString(handler).c_str());
+}
+
+template<>
+void SupportMgr::ShowList<SuggestionTicket>(ChatHandler& handler) const
+{
+ handler.SendSysMessage(LANG_COMMAND_TICKETSHOWLIST);
+ for (SuggestionTicketList::const_iterator itr = _suggestionTicketList.begin(); itr != _suggestionTicketList.end(); ++itr)
+ if (!itr->second->IsClosed())
+ handler.SendSysMessage(itr->second->FormatViewMessageString(handler).c_str());
+}
+
+template<>
+void SupportMgr::ShowClosedList<GmTicket>(ChatHandler& handler) const
+{
+ handler.SendSysMessage(LANG_COMMAND_TICKETSHOWCLOSEDLIST);
+ for (GmTicketList::const_iterator itr = _gmTicketList.begin(); itr != _gmTicketList.end(); ++itr)
+ if (itr->second->IsClosed())
+ handler.SendSysMessage(itr->second->FormatViewMessageString(handler).c_str());
+}
+
+template<>
+void SupportMgr::ShowClosedList<BugTicket>(ChatHandler& handler) const
+{
+ handler.SendSysMessage(LANG_COMMAND_TICKETSHOWCLOSEDLIST);
+ for (BugTicketList::const_iterator itr = _bugTicketList.begin(); itr != _bugTicketList.end(); ++itr)
+ if (itr->second->IsClosed())
+ handler.SendSysMessage(itr->second->FormatViewMessageString(handler).c_str());
+}
+
+template<>
+void SupportMgr::ShowClosedList<ComplaintTicket>(ChatHandler& handler) const
+{
+ handler.SendSysMessage(LANG_COMMAND_TICKETSHOWCLOSEDLIST);
+ for (ComplaintTicketList::const_iterator itr = _complaintTicketList.begin(); itr != _complaintTicketList.end(); ++itr)
+ if (itr->second->IsClosed())
+ handler.SendSysMessage(itr->second->FormatViewMessageString(handler).c_str());
+}
+
+template<>
+void SupportMgr::ShowClosedList<SuggestionTicket>(ChatHandler& handler) const
+{
+ handler.SendSysMessage(LANG_COMMAND_TICKETSHOWCLOSEDLIST);
+ for (SuggestionTicketList::const_iterator itr = _suggestionTicketList.begin(); itr != _suggestionTicketList.end(); ++itr)
+ if (itr->second->IsClosed())
+ handler.SendSysMessage(itr->second->FormatViewMessageString(handler).c_str());
+}
+
+void SupportMgr::ShowGmEscalatedList(ChatHandler& handler) const
+{
+ handler.SendSysMessage(LANG_COMMAND_TICKETSHOWESCALATEDLIST);
+ for (GmTicketList::const_iterator itr = _gmTicketList.begin(); itr != _gmTicketList.end(); ++itr)
+ if (!itr->second->IsClosed() && itr->second->GetEscalatedStatus() == TICKET_IN_ESCALATION_QUEUE)
+ handler.SendSysMessage(itr->second->FormatViewMessageString(handler).c_str());
+}
+
+void SupportMgr::SendGmTicket(WorldSession* session, GmTicket* ticket) const
+{
+ WorldPackets::Ticket::GMTicketGetTicketResponse response;
+
+ if (ticket)
+ {
+ response.Result = GMTICKET_STATUS_HASTEXT;
+ response.Info.HasValue = true;
+
+ response.Info.Value.TicketID = ticket->GetId();
+ response.Info.Value.TicketDescription = ticket->GetDescription();
+ response.Info.Value.Category = ticket->GetEscalatedStatus();
+ response.Info.Value.TicketOpenTime = GetAge(ticket->GetLastModifiedTime());
+ response.Info.Value.OldestTicketTime = sSupportMgr->GetOldestOpenTicket() ? GetAge(sSupportMgr->GetOldestOpenTicket()->GetLastModifiedTime()) : float(0);
+ response.Info.Value.UpdateTime = GetAge(sSupportMgr->GetLastChange());
+ response.Info.Value.AssignedToGM = ticket->IsAssigned();
+ response.Info.Value.OpenedByGM = ticket->IsViewed();
+ response.Info.Value.WaitTimeOverrideMessage = "";
+ response.Info.Value.WaitTimeOverrideMinutes = 0;
+ }
+ else
+ response.Result = GMTICKET_STATUS_DEFAULT;
+
+ session->SendPacket(response.Write());
+}
+
+void SupportMgr::SendGmTicketUpdate(WorldSession* session, GMTicketResponse response) const
+{
+ WorldPackets::Ticket::GMTicketUpdate update;
+ update.Result = response;
+
+ session->SendPacket(update.Write());
+}
diff --git a/src/server/game/Support/SupportMgr.h b/src/server/game/Support/SupportMgr.h
new file mode 100644
index 00000000000..eddb3cebf75
--- /dev/null
+++ b/src/server/game/Support/SupportMgr.h
@@ -0,0 +1,416 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ *
+ * 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 2 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef SupportMgr_h__
+#define SupportMgr_h__
+
+#include "ObjectMgr.h"
+#include "Player.h"
+#include "TicketPackets.h"
+
+class ChatHandler;
+
+// from blizzard lua
+enum GMTicketSystemStatus
+{
+ GMTICKET_QUEUE_STATUS_DISABLED = 0,
+ GMTICKET_QUEUE_STATUS_ENABLED = 1
+};
+
+enum GMTicketStatus
+{
+ GMTICKET_STATUS_HASTEXT = 0x06,
+ GMTICKET_STATUS_DEFAULT = 0x0A
+};
+
+enum GMTicketResponse
+{
+ GMTICKET_RESPONSE_ALREADY_EXIST = 1,
+ GMTICKET_RESPONSE_CREATE_SUCCESS = 2,
+ GMTICKET_RESPONSE_CREATE_ERROR = 3,
+ GMTICKET_RESPONSE_UPDATE_SUCCESS = 4,
+ GMTICKET_RESPONSE_UPDATE_ERROR = 5,
+ GMTICKET_RESPONSE_TICKET_DELETED = 9
+};
+
+// from blizzard lua
+enum GMTicketOpenedByGMStatus
+{
+ GMTICKET_OPENEDBYGM_STATUS_NOT_OPENED = 0, // ticket has never been opened by a gm
+ GMTICKET_OPENEDBYGM_STATUS_OPENED = 1 // ticket has been opened by a gm
+};
+
+// from Blizzard LUA:
+// GMTICKET_ASSIGNEDTOGM_STATUS_NOT_ASSIGNED = 0; -- ticket is not currently assigned to a gm
+// GMTICKET_ASSIGNEDTOGM_STATUS_ASSIGNED = 1; -- ticket is assigned to a normal gm
+// GMTICKET_ASSIGNEDTOGM_STATUS_ESCALATED = 2; -- ticket is in the escalation queue
+// 3 is a custom value and should never actually be sent
+enum GMTicketEscalationStatus
+{
+ TICKET_UNASSIGNED = 0,
+ TICKET_ASSIGNED = 1,
+ TICKET_IN_ESCALATION_QUEUE = 2,
+ TICKET_ESCALATED_ASSIGNED = 3
+};
+
+enum GMSupportComplaintType
+{
+ GMTICKET_SUPPORT_COMPLAINT_TYPE_NONE = 0,
+ GMTICKET_SUPPORT_COMPLAINT_TYPE_LANGUAGE = 2,
+ GMTICKET_SUPPORT_COMPLAINT_TYPE_PLAYERNAME = 4,
+ GMTICKET_SUPPORT_COMPLAINT_TYPE_CHEAT = 15,
+ GMTICKET_SUPPORT_COMPLAINT_TYPE_GUILDNAME = 23,
+ GMTICKET_SUPPORT_COMPLAINT_TYPE_SPAMMING = 24
+};
+
+using ChatLog = WorldPackets::Ticket::SupportTicketSubmitComplaint::SupportTicketChatLog;
+
+class Ticket
+{
+public:
+ Ticket();
+ Ticket(Player* player);
+ virtual ~Ticket();
+
+ bool IsClosed() const { return !_closedBy.IsEmpty(); }
+ bool IsFromPlayer(ObjectGuid guid) const { return guid == _playerGuid; }
+ bool IsAssigned() const { return !_assignedTo.IsEmpty(); }
+ bool IsAssignedTo(ObjectGuid guid) const { return guid == _assignedTo; }
+ bool IsAssignedNotTo(ObjectGuid guid) const { return IsAssigned() && !IsAssignedTo(guid); }
+
+ uint32 GetId() const { return _id; }
+ ObjectGuid GetPlayerGuid() const { return _playerGuid; }
+ Player* GetPlayer() const { return ObjectAccessor::FindPlayer(_playerGuid); }
+ Player* GetAssignedPlayer() const { return ObjectAccessor::FindPlayer(_assignedTo); }
+ ObjectGuid GetAssignedToGUID() const { return _assignedTo; }
+ std::string GetAssignedToName() const
+ {
+ std::string name;
+ if (!_assignedTo.IsEmpty())
+ ObjectMgr::GetPlayerNameByGUID(_assignedTo, name);
+
+ return name;
+ }
+ std::string const& GetComment() { return _comment; }
+
+ virtual void SetAssignedTo(ObjectGuid guid) { _assignedTo = guid; }
+ virtual void SetAssignedTo(ObjectGuid /*guid*/, bool /*isAdmin*/) { }
+ virtual void SetUnassigned() { _assignedTo.Clear(); }
+ void SetClosedBy(ObjectGuid value) { _closedBy = value; }
+ void SetComment(std::string const& comment) { _comment = comment; }
+ void SetPosition(uint32 mapId, G3D::Vector3& pos)
+ {
+ _mapId = mapId;
+ _pos = pos;
+ }
+
+ virtual void LoadFromDB(Field* fields) = 0;
+ virtual void SaveToDB(SQLTransaction& trans) const = 0;
+ virtual void DeleteFromDB() = 0;
+
+ void TeleportTo(Player* player) const;
+
+ virtual std::string FormatViewMessageString(ChatHandler& handler, bool detailed = false) const = 0;
+ virtual std::string FormatViewMessageString(ChatHandler& handler, const char* szClosedName, const char* szAssignedToName, const char* szUnassignedName, const char* szDeletedName, const char* szCompletedName) const;
+
+protected:
+ uint32 _id;
+ ObjectGuid _playerGuid;
+ uint16 _mapId;
+ G3D::Vector3 _pos;
+ uint64 _createTime;
+ ObjectGuid _closedBy; // 0 = Open, -1 = Console, playerGuid = player abandoned ticket, other = GM who closed it.
+ ObjectGuid _assignedTo;
+ std::string _comment;
+};
+
+
+class GmTicket : public Ticket
+{
+public:
+ GmTicket();
+ GmTicket(Player* player);
+ ~GmTicket();
+
+ bool IsCompleted() const { return _completed; }
+ bool IsViewed() const { return _viewed; }
+
+ bool GetNeedMoreHelp() const { return _needMoreHelp; }
+ std::string const& GetDescription() const { return _description; }
+ uint64 GetLastModifiedTime() const { return _lastModifiedTime; }
+ GMTicketEscalationStatus GetEscalatedStatus() const { return _escalatedStatus; }
+ std::string const& GetResponse() const { return _response; }
+
+ void SetAssignedTo(ObjectGuid guid, bool isAdmin) override
+ {
+ _assignedTo = guid;
+ if (isAdmin && _escalatedStatus == TICKET_IN_ESCALATION_QUEUE)
+ _escalatedStatus = TICKET_ESCALATED_ASSIGNED;
+ else if (_escalatedStatus == TICKET_UNASSIGNED)
+ _escalatedStatus = TICKET_ASSIGNED;
+ }
+ void SetEscalatedStatus(GMTicketEscalationStatus escalatedStatus) { _escalatedStatus = escalatedStatus; }
+ void SetCompleted() { _completed = true; }
+ void SetDescription(std::string const& description)
+ {
+ _description = description;
+ _lastModifiedTime = uint64(time(NULL));
+ }
+ void SetViewed() { _viewed = true; }
+ void SetGmAction(uint32 needResponse, bool needMoreHelp);
+ void SetUnassigned() override;
+
+ void AppendResponse(std::string const& response) { _response += response; }
+
+ void SetChatLog(std::list<uint32> time, std::string const& log);
+ std::string const& GetChatLog() const { return _chatLog; }
+
+ void SendResponse(WorldSession* session) const;
+
+ void LoadFromDB(Field* fields) override;
+ void SaveToDB(SQLTransaction& trans) const override;
+ void DeleteFromDB() override;
+
+ std::string FormatViewMessageString(ChatHandler& handler, bool detailed = false) const override;
+ std::string FormatViewMessageString(ChatHandler& handler, const char* szClosedName, const char* szAssignedToName, const char* szUnassignedName, const char* szDeletedName, const char* szCompletedName) const override;
+
+private:
+ std::string _description;
+ uint64 _lastModifiedTime;
+ bool _completed;
+ GMTicketEscalationStatus _escalatedStatus;
+ bool _viewed;
+ bool _needResponse; /// @todo find out the use of this, and then store it in DB
+ bool _needMoreHelp;
+ std::string _response;
+ std::string _chatLog; // No need to store in db, will be refreshed every session client side
+};
+
+class BugTicket : public Ticket
+{
+public:
+ BugTicket();
+ BugTicket(Player* player);
+ ~BugTicket();
+
+ std::string const& GetNote() const { return _note; }
+
+ void SetFacing(float facing) { _facing = facing; }
+ void SetNote(std::string const& note) { _note = note; }
+
+ void LoadFromDB(Field* fields) override;
+ void SaveToDB(SQLTransaction& trans) const override;
+ void DeleteFromDB() override;
+
+ using Ticket::FormatViewMessageString;
+ std::string FormatViewMessageString(ChatHandler& handler, bool detailed = false) const override;
+
+private:
+ float _facing;
+ std::string _note;
+};
+
+class ComplaintTicket : public Ticket
+{
+public:
+ ComplaintTicket();
+ ComplaintTicket(Player* player);
+ ~ComplaintTicket();
+
+ ObjectGuid GetTargetCharacterGuid() const { return _targetCharacterGuid; }
+ GMSupportComplaintType GetComplaintType() const { return _complaintType; }
+ std::string const& GetNote() const { return _note; }
+
+ void SetFacing(float facing) { _facing = facing; }
+ void SetTargetCharacterGuid(ObjectGuid targetCharacterGuid)
+ {
+ _targetCharacterGuid = targetCharacterGuid;
+ }
+ void SetComplaintType(GMSupportComplaintType type) { _complaintType = type; }
+ void SetChatLog(ChatLog const& log) { _chatLog = log; }
+ void SetNote(std::string const& note) { _note = note; }
+
+ void LoadFromDB(Field* fields) override;
+ void LoadChatLineFromDB(Field* fields);
+ void SaveToDB(SQLTransaction& trans) const override;
+ void DeleteFromDB() override;
+
+ using Ticket::FormatViewMessageString;
+ std::string FormatViewMessageString(ChatHandler& handler, bool detailed = false) const override;
+
+private:
+ float _facing;
+ ObjectGuid _targetCharacterGuid;
+ GMSupportComplaintType _complaintType;
+ ChatLog _chatLog;
+ std::string _note;
+};
+
+class SuggestionTicket : public Ticket
+{
+public:
+ SuggestionTicket();
+ SuggestionTicket(Player* player);
+ ~SuggestionTicket();
+
+ std::string const& GetNote() const { return _note; }
+ void SetNote(std::string const& note) { _note = note; }
+
+ void SetFacing(float facing) { _facing = facing; }
+
+ void LoadFromDB(Field* fields) override;
+ void SaveToDB(SQLTransaction& trans) const override;
+ void DeleteFromDB() override;
+
+ using Ticket::FormatViewMessageString;
+ std::string FormatViewMessageString(ChatHandler& handler, bool detailed = false) const override;
+
+private:
+ float _facing;
+ std::string _note;
+};
+
+typedef std::map<uint32, BugTicket*> BugTicketList;
+typedef std::map<uint32, ComplaintTicket*> ComplaintTicketList;
+typedef std::map<uint32, SuggestionTicket*> SuggestionTicketList;
+typedef std::map<uint32, GmTicket*> GmTicketList;
+
+class SupportMgr
+{
+private:
+ SupportMgr();
+ ~SupportMgr();
+
+public:
+ static SupportMgr* instance()
+ {
+ static SupportMgr instance;
+ return &instance;
+ }
+
+ template<typename T>
+ T* GetTicket(uint32 ticketId);
+
+ GmTicket* GetGmTicketByPlayerGuid(ObjectGuid playerGuid) const
+ {
+ for (auto const& c : _gmTicketList)
+ if (c.second->GetPlayerGuid() == playerGuid && !c.second->IsClosed())
+ return c.second;
+
+ return nullptr;
+ }
+
+ ComplaintTicketList GetComplaintsByPlayerGuid(ObjectGuid playerGuid) const
+ {
+ ComplaintTicketList ret;
+ for (auto const& c : _complaintTicketList)
+ if (c.second->GetPlayerGuid() == playerGuid)
+ ret.insert(c);
+
+ return ret;
+ }
+
+ GmTicket* GetOldestOpenTicket()
+ {
+ for (GmTicketList::const_iterator itr = _gmTicketList.begin(); itr != _gmTicketList.end(); ++itr)
+ if (itr->second && !itr->second->IsClosed() && !itr->second->IsCompleted())
+ return itr->second;
+
+ return nullptr;
+ }
+
+ void Initialize();
+
+ bool GetSupportSystemStatus() { return _supportSystemStatus; }
+ bool GetTicketSystemStatus() { return _supportSystemStatus && _ticketSystemStatus; }
+ bool GetBugSystemStatus() { return _supportSystemStatus && _bugSystemStatus; }
+ bool GetComplaintSystemStatus() { return _supportSystemStatus && _complaintSystemStatus; }
+ bool GetSuggestionSystemStatus() { return _supportSystemStatus && _suggestionSystemStatus; }
+ uint64 GetLastChange() const { return _lastChange; }
+ template<typename T>
+ uint32 GetOpenTicketCount() const;
+
+ void SetSupportSystemStatus(bool status) { _supportSystemStatus = status; }
+ void SetTicketSystemStatus(bool status) { _ticketSystemStatus = status; }
+ void SetBugSystemStatus(bool status) { _bugSystemStatus = status; }
+ void SetComplaintSystemStatus(bool status) { _complaintSystemStatus = status; }
+ void SetSuggestionSystemStatus(bool status) { _suggestionSystemStatus = status; }
+
+ void LoadGmTickets();
+ void LoadBugTickets();
+ void LoadComplaintTickets();
+ void LoadSuggestionTickets();
+
+ void AddTicket(GmTicket* ticket);
+ void AddTicket(BugTicket* ticket);
+ void AddTicket(ComplaintTicket* ticket);
+ void AddTicket(SuggestionTicket* ticket);
+
+ template<typename T>
+ void RemoveTicket(uint32 ticketId);
+
+ template<typename T>
+ void CloseTicket(uint32 ticketId, ObjectGuid closedBy);
+
+ template<typename T>
+ void ResetTickets();
+
+ template<typename T>
+ void ShowList(ChatHandler& handler) const;
+
+ template<typename T>
+ void ShowList(ChatHandler& handler, bool onlineOnly) const;
+
+ template<typename T>
+ void ShowClosedList(ChatHandler& handler) const;
+
+ void ShowGmEscalatedList(ChatHandler& handler) const;
+
+ void UpdateLastChange() { _lastChange = uint64(time(nullptr)); }
+
+ void SendGmTicket(WorldSession* session, GmTicket* ticket) const;
+ void SendGmTicketUpdate(WorldSession* session, GMTicketResponse response) const;
+
+ uint32 GenerateGmTicketId() { return ++_lastGmTicketId; }
+ uint32 GenerateBugId() { return ++_lastBugId; }
+ uint32 GenerateComplaintId() { return ++_lastComplaintId; }
+ uint32 GenerateSuggestionId() { return ++_lastSuggestionId; }
+
+private:
+ bool _supportSystemStatus;
+ bool _ticketSystemStatus;
+ bool _bugSystemStatus;
+ bool _complaintSystemStatus;
+ bool _suggestionSystemStatus;
+ GmTicketList _gmTicketList;
+ BugTicketList _bugTicketList;
+ ComplaintTicketList _complaintTicketList;
+ SuggestionTicketList _suggestionTicketList;
+ uint32 _lastGmTicketId;
+ uint32 _lastBugId;
+ uint32 _lastComplaintId;
+ uint32 _lastSuggestionId;
+ uint32 _openGmTicketCount;
+ uint32 _openBugTicketCount;
+ uint32 _openComplaintTicketCount;
+ uint32 _openSuggestionTicketCount;
+ uint64 _lastChange;
+};
+
+#define sSupportMgr SupportMgr::instance()
+
+#endif // SupportMgr_h__