diff --git a/[admin]/admin2/admin_definitions.lua b/[admin]/admin2/admin_definitions.lua index 9e7951eb5..67dfbecad 100644 --- a/[admin]/admin2/admin_definitions.lua +++ b/[admin]/admin2/admin_definitions.lua @@ -46,7 +46,8 @@ enum( "EVENT_SCREEN_SHOT", "EVENT_RESOURCE_START", "EVENT_RESOURCE_STOP", - "EVENT_PLAYER_JOIN" + "EVENT_PLAYER_JOIN", + "EVENT_MUTE" }, "ae" ) @@ -64,7 +65,8 @@ enum( "SYNC_SERVER", "SYNC_BAN", "SYNC_BANS", - "SYNC_MESSAGES" + "SYNC_MESSAGES", + "SYNC_MUTES" }, "as" ) diff --git a/[admin]/admin2/client/main/admin.lua b/[admin]/admin2/client/main/admin.lua index 142aeffb0..4755892ff 100644 --- a/[admin]/admin2/client/main/admin.lua +++ b/[admin]/admin2/client/main/admin.lua @@ -18,6 +18,7 @@ aAdminMain = { BlockedTabsBySensitiveData = { ['bans'] = true, ['adminchat'] = true, + ['mutes'] = true } } @@ -53,6 +54,7 @@ function aAdminMain.Open() aAdminMain.AddTab("Resources", aResourcesTab, "resources") aAdminMain.AddTab("Server", aServerTab, "server") aAdminMain.AddTab("Bans", aBansTab, "bans") + aAdminMain.AddTab("Mutes", aMuteTab, "mutes") aAdminMain.AddTab("Admin Chat", aChatTab, "adminchat") aAdminMain.AddTab("Rights", aAclTab, "acl") -- aAdminMain.AddTab("Network", aNetworkTab) diff --git a/[admin]/admin2/client/main/admin_mute.lua b/[admin]/admin2/client/main/admin_mute.lua new file mode 100644 index 000000000..00bfa7ccb --- /dev/null +++ b/[admin]/admin2/client/main/admin_mute.lua @@ -0,0 +1,109 @@ +--[[********************************** +* +* Multi Theft Auto - Admin Panel +* +* client\main\admin_mute.lua +* +* Original File by omar-o22 +* +**************************************]] +aMuteTab = { + List = {} +} + +function aMuteTab.Create(tab) + aMuteTab.Tab = tab + + aMuteTab.MuteListSearch = guiCreateEdit(0.01, 0.02, 0.3, 0.04, "", true, aMuteTab.Tab) + guiCreateInnerImage("client\\images\\search.png", aMuteTab.MuteListSearch) + guiHandleInput(aMuteTab.MuteListSearch) + aMuteTab.MuteList = guiCreateGridList(0.01, 0.07, 0.80, 0.91, true, aMuteTab.Tab) + guiGridListAddColumn(aMuteTab.MuteList, "Name", 0.22) + guiGridListAddColumn(aMuteTab.MuteList, "Serial", 0.25) + guiGridListAddColumn(aMuteTab.MuteList, "Duration", 0.17) + guiGridListAddColumn(aMuteTab.MuteList, "Muted by", 0.22) + aMuteTab.Details = guiCreateButton(0.82, 0.07, 0.17, 0.04, "Details", true, aMuteTab.Tab) + aMuteTab.Unmute = guiCreateButton(0.82, 0.12, 0.17, 0.04, "Unmute", true, aMuteTab.Tab, "unmute") + aMuteTab.MuteRefresh = guiCreateButton(0.82, 0.94, 0.17, 0.04, "Refresh", true, aMuteTab.Tab, "listmute") + + addEventHandler("onClientGUIChanged", aMuteTab.MuteListSearch, aMuteTab.onMuteListSearch) + addEventHandler("onClientGUIClick", aMuteTab.Tab, aMuteTab.onClientClick) + addEventHandler(EVENT_SYNC, root, aMuteTab.onClientSync) + + guiGridListClear(aMuteTab.MuteList) + sync(SYNC_MUTES) +end + +function aMuteTab.onClientClick(button) + if (button == "left") then + if (source == aMuteTab.Details) then + local row = guiGridListGetSelectedItem(aMuteTab.MuteList) + if (row == -1) then + messageBox("No mute selected!", MB_ERROR, MB_OK) + return + end + + local serial = guiGridListGetItemText(aMuteTab.MuteList, row, 2) + aMuteDetails.Show(serial) + elseif (source == aMuteTab.Unmute) then + local row = guiGridListGetSelectedItem(aMuteTab.MuteList) + if (row == -1) then + messageBox("No mute selected!", MB_ERROR, MB_OK) + return + end + + local serial = guiGridListGetItemText(aMuteTab.MuteList, row, 2) + aMuteDetails.Show(serial, true) + elseif (source == aMuteTab.MuteRefresh) then + guiGridListClear(aMuteTab.MuteList) + sync(SYNC_MUTES) + end + end +end + +function aMuteTab.onMuteListSearch() + guiGridListClear(aMuteTab.MuteList) + local text = string.upper(guiGetText(source)) + if (text == "") then + aMuteTab.Refresh() + else + for serial, mute in pairs(aMuteTab.List) do + if + ((mute.name and string.find(string.upper(mute.name), text)) or + (serial and string.find(string.upper(serial), text)) or + (mute.admin and string.find(string.upper(mute.admin), text))) + then + aMuteTab.AddRow(serial, mute) + end + end + end +end + +function aMuteTab.onClientSync(type, data) + if (type == SYNC_MUTES) then + aMuteTab.List = data + aMuteTab.Refresh() + end +end + +function aMuteTab.Refresh() + guiGridListClear(aMuteTab.MuteList) + for id, ban in pairs(aMuteTab.List) do + aMuteTab.AddRow(id, ban) + end +end + +function aMuteTab.AddRow(serial, data) + local list = aMuteTab.MuteList + local row = guiGridListAddRow(list) + local time + if time == 0 then + time = "Permanent" + else + time = secondsToTimeDesc(data.time / 1000) + end + guiGridListSetItemText(list, row, 1, data.name or "Unknown", false, false) + guiGridListSetItemText(list, row, 2, serial or "", false, false) + guiGridListSetItemText(list, row, 3, time, false, false) + guiGridListSetItemText(list, row, 4, data.admin or "Console", false, false) +end \ No newline at end of file diff --git a/[admin]/admin2/client/main/admin_players.lua b/[admin]/admin2/client/main/admin_players.lua index 3aeb0afe0..e222cb97f 100644 --- a/[admin]/admin2/client/main/admin_players.lua +++ b/[admin]/admin2/client/main/admin_players.lua @@ -174,12 +174,11 @@ function aPlayersTab.onClientClick(button) guiComboBoxGetItemText(aPlayersTab.SlapOptions, guiComboBoxGetSelected(aPlayersTab.SlapOptions)) ) elseif (source == aPlayersTab.Mute) then - triggerServerEvent( - "aPlayer", - localPlayer, - player, - iif(aPlayers[player].mute, "unmute", "mute") - ) + if aPlayers[player].mute then + triggerServerEvent(EVENT_MUTE, localPlayer, "unmute", {serial = getPlayerSerial(player)}) + else + aMute.Show(player) + end elseif (source == aPlayersTab.Freeze) then triggerServerEvent( "aPlayer", diff --git a/[admin]/admin2/client/widgets/admin_mute.lua b/[admin]/admin2/client/widgets/admin_mute.lua new file mode 100644 index 000000000..baf01d0d3 --- /dev/null +++ b/[admin]/admin2/client/widgets/admin_mute.lua @@ -0,0 +1,253 @@ +--[[********************************** +* +* Multi Theft Auto - Admin Panel +* +* client\widgets\admin_mute.lua +* +* Original File by omar-o22 +* +**************************************]] +aMute = { + defaultDurations = { + {"1 minute", 1}, + {"1 hour", 60}, + {"1 day", 60 * 24}, + {"1 week", 60 * 24 * 7}, + {"Permanent", 0}, -- HARDCODED AS SECOND LAST IN THIS TABLE, DO NOT MOVE + {"Custom", 0} -- HARDCODED AS LAST LAST IN THIS TABLE, DO NOT MOVE + }, + durationType = "minutes", + Form = nil, + playerName = nil +} + +function aMute.Show(player) + if (not aMute.Form) then + aMute.Create() + end + + if (player) then + aMute.playerName = getPlayerName(player) + guiSetText(aMute.Form, "Mute player "..aMute.playerName) + end + + addEventHandler("onClientGUIClick", aMute.Form, aMute.onClick) + addEventHandler("onClientGUIFocus", aMute.Form, aMute.onFocus) + addEventHandler("onClientGUIBlur", aMute.Form, aMute.onBlur) + guiSetVisible(aMute.Form, true) + guiBringToFront(aMute.Form) +end + +function aMute.Close(destroy) + if (destroy) then + destroyElement(aMute.Form) + aMute.Form = nil + else + removeEventHandler("onClientGUIClick", aMute.Form, aMute.onClick) + guiSetVisible(aMute.Form, false) + aMute.Reset() + end + aMute.playerName = nil +end + +function aMute.Create() + local sx, sy = guiGetScreenSize() + + aMute.Form = guiCreateWindow((sx - 350) / 2, (sy - 290) / 2, 350, 290, "Mute player", false) + aMute.ReasonLabel = guiCreateLabel(25, 40, 300, 20, "Mute reason (required):", false, aMute.Form) + aMute.ReasonEditBox = guiCreateEdit(25, 65, 300, 30, "Enter mute reason...", false, aMute.Form) + aMute.ReasonEditBoxRecievedInput = false + aMute.DurationLabel = guiCreateLabel(25, 110, 300, 20, "Mute duration (required):", false, aMute.Form) + aMute.DurationComboBox = guiCreateComboBox(25, 140, 300, 100, "Select mute duration...", false, aMute.Form) + for i=1, #aMute.defaultDurations do + guiComboBoxAddItem(aMute.DurationComboBox, aMute.defaultDurations[i][1]) + end + aMute.DurationEditBox = guiCreateEdit(25, 175, 300, 30, "Duration...", false, aMute.Form) + aMute.DurationEditBoxRecievedInput = false + guiSetEnabled(aMute.DurationEditBox, false) + + aMute.RadioSeconds = guiCreateRadioButton(35, 216, 70, 20, "Seconds", false, aMute.Form) + guiSetEnabled(aMute.RadioSeconds, false) + aMute.RadioMinutes = guiCreateRadioButton(112, 216, 70, 20, "Minutes", false, aMute.Form) + guiSetEnabled(aMute.RadioMinutes, false) + aMute.RadioHours = guiCreateRadioButton(189, 216, 70, 20, "Hours", false, aMute.Form) + guiSetEnabled(aMute.RadioHours, false) + aMute.RadioDays = guiCreateRadioButton(256, 216, 70, 20, "Days", false, aMute.Form) + guiSetEnabled(aMute.RadioDays, false) + + aMute.SubmitButton = guiCreateButton(70, 246, 100, 30, "Submit", false, aMute.Form) + aMute.CancelButton = guiCreateButton(180, 246, 100, 30, "Cancel", false, aMute.Form) + aRegister("mute", aMute.Form, aMute.Show, aMute.Close) +end + +function aMute.Reset() + guiSetText(aMute.Form, "Mute player") + guiSetText(aMute.ReasonEditBox, "Enter mute reason...") + aMute.ReasonEditBoxRecievedInput = false + guiComboBoxSetSelected(aMute.DurationComboBox, -1) + guiSetText(aMute.DurationEditBox, "Duration...") + guiSetEnabled(aMute.DurationEditBox, false) + aMute.DurationEditBoxRecievedInput = false + guiSetEnabled(aMute.RadioSeconds, false) + guiSetEnabled(aMute.RadioMinutes, false) + guiSetEnabled(aMute.RadioHours, false) + guiSetEnabled(aMute.RadioDays, false) + aMute.durationType = "minutes" +end + +function aMute.onClick(button, state) + if not (button == "left" and state == "up") then + return + end + + -- Handle cancel button first + if source == aMute.CancelButton then + aMute.Close() + return + end + + -- Autofill and enable/disable duration editbox based on choice + if source == aMute.DurationComboBox then + local selected = guiComboBoxGetSelected(aMute.DurationComboBox) + if selected == -1 then + return + elseif selected == #aMute.defaultDurations - 2 then + -- Second-last option is permanent duration - clear and disable edit box + guiSetText(aMute.DurationEditBox, "0") + guiSetEnabled(aMute.DurationEditBox, false) + aMute.durationType = "minutes" + elseif selected == #aMute.defaultDurations - 1 then + -- Last option (should) be custom duration - enable duration edit box + guiSetText(aMute.DurationEditBox, "Duration...") + guiSetEnabled(aMute.DurationEditBox, true) + guiSetEnabled(aMute.RadioSeconds, true) + guiSetEnabled(aMute.RadioMinutes, true) + guiSetEnabled(aMute.RadioHours, true) + guiSetEnabled(aMute.RadioDays, true) + guiRadioButtonSetSelected(aMute.RadioMinutes, true) + aMute.durationType = "minutes" + aMute.DurationEditBoxRecievedInput = false + else + guiSetText(aMute.DurationEditBox, aMute.defaultDurations[selected + 1][2]) + guiSetEnabled(aMute.DurationEditBox, false) + guiSetEnabled(aMute.RadioSeconds, false) + guiSetEnabled(aMute.RadioMinutes, false) + guiSetEnabled(aMute.RadioHours, false) + guiSetEnabled(aMute.RadioDays, false) + aMute.durationType = "minutes" + end + return + end + + if source == aMute.RadioSeconds or source == aMute.RadioMinutes or source == aMute.RadioHours or source == aMute.RadioDays then + if guiRadioButtonGetSelected(aMute.RadioSeconds) then + aMute.durationType = "seconds" + return + elseif guiRadioButtonGetSelected(aMute.RadioMinutes) then + aMute.durationType = "minutes" + return + elseif guiRadioButtonGetSelected(aMute.RadioHours) then + aMute.durationType = "hours" + return + elseif guiRadioButtonGetSelected(aMute.RadioDays) then + aMute.durationType = "days" + return + end + end + + -- Handle submit button + if source == aMute.SubmitButton then + aMute.verifyForm() + return + end +end + +function aMute.onFocus() + -- Clear reason/duration edit boxes on first click + if source == aMute.ReasonEditBox then + if not aMute.ReasonEditBoxRecievedInput then + guiSetText(aMute.ReasonEditBox, "") + aMute.ReasonEditBoxRecievedInput = true + end + elseif source == aMute.DurationEditBox then + if not aMute.DurationEditBoxRecievedInput then + guiSetText(aMute.DurationEditBox, "") + aMute.DurationEditBoxRecievedInput = true + end + end +end + +function aMute.onBlur() + -- Reset default text of reason/duration edit boxes if they lose focus with no input + if source == aMute.ReasonEditBox then + if guiGetText(source) == "" then + guiSetText(aMute.ReasonEditBox, "Enter mute reason...") + aMute.ReasonEditBoxRecievedInput = false + end + elseif source == aMute.DurationEditBox then + if guiGetText(source) == "" and (guiComboBoxGetSelected(aMute.DurationComboBox) == #aMute.defaultDurations - 1) then + guiSetText(aMute.DurationEditBox, "Duration (seconds)...") + aMute.DurationEditBoxRecievedInput = false + end + end +end + +function aMute.verifyForm() + -- Verify mute reason + local muteReason = guiGetText(aMute.ReasonEditBox) + if muteReason == "" or (not aMute.ReasonEditBoxRecievedInput) then + messageBox("No mute reason provided.", MB_ERROR, MB_OK) + return + end + + -- Verify mute duration + local muteDuration + local durationSelection = guiComboBoxGetSelected(aMute.DurationComboBox) + if durationSelection == -1 then + messageBox("No mute duration provided.", MB_ERROR, MB_OK) + return + end + durationSelection = durationSelection + 1 -- ComboBox item indices starts at 0 instead of one + if durationSelection == #aMute.defaultDurations then + muteDuration = guiGetText(aMute.DurationEditBox) + muteDuration = tonumber(muteDuration) + if not muteDuration or muteDuration <= 0 then + messageBox("Invalid mute duration provided.", MB_ERROR, MB_OK) + return + end + else + muteDuration = aMute.defaultDurations[durationSelection][2] + end + + -- Build mute request "packet" and send to server + local actualPlayer -- Actual player may be offline + if aMute.playerName then + actualPlayer = getPlayerFromName(aMute.playerName) + end + + local time + if aMute.durationType == "seconds" then + time = muteDuration * 1000 + elseif aMute.durationType == "minutes" then + time = muteDuration * 60 * 1000 + elseif aMute.durationType == "hours" then + time = muteDuration * 60 * 60 * 1000 + elseif aMute.durationType == "days" then + time = muteDuration * 60 * 60 * 24 * 1000 + end + + local data = { + playerName = aMute.playerName, + reason = muteReason, + duration = time, + player = actualPlayer + } + + triggerServerEvent( + EVENT_MUTE, + localPlayer, + "mute", + data + ) + aMute.Close() +end \ No newline at end of file diff --git a/[admin]/admin2/client/widgets/admin_mute_details.lua b/[admin]/admin2/client/widgets/admin_mute_details.lua new file mode 100644 index 000000000..08a8d1911 --- /dev/null +++ b/[admin]/admin2/client/widgets/admin_mute_details.lua @@ -0,0 +1,110 @@ +--[[********************************** +* +* Multi Theft Auto - Admin Panel +* +* client\widgets\admin_mute_details.lua +* +* Original File by omar-o22 +* +**************************************]] +aMuteDetails = { + Form = nil, + Serial = nil +} + +function aMuteDetails.Show(Serial, showUnmtue) + if not aMuteDetails.Form then + aMuteDetails.Create() + end + + aMuteDetails.Serial = Serial + local data = aMuteTab.List[Serial] + + guiSetText(aMuteDetails.NickText, "Player name: "..(data.name or "Unknown")) + guiSetText(aMuteDetails.SerialText, "Serial: "..(Serial or "None")) + guiSetText(aMuteDetails.ReasonText, "Reason: "..(data.reason or "None")) + guiSetText(aMuteDetails.AdminText, "Responsible admin: "..(data.admin or "Unknown")) + local time + if time == 0 then + time = "Permanent" + else + time = secondsToTimeDesc(data.time / 1000) + end + guiSetText(aMuteDetails.DurationText, "Duration: "..time) + + addEventHandler("onClientGUIClick", aMuteDetails.Form, aMuteDetails.onClick) + guiSetVisible(aMuteDetails.Form, true) + + -- Toggle visibilty of certain elements if this is being opened as a confimration dialog for unmute action + if showUnmtue then + guiSetText(aMuteDetails.ConfirmationText, "Are you sure you want to remove this mute?") + guiSetVisible(aMuteDetails.CloseButton, false) + guiSetVisible(aMuteDetails.SubmitButton, true) + guiSetVisible(aMuteDetails.CancelButton, true) + end + guiBringToFront(aMuteDetails.Form) +end + +function aMuteDetails.Close(destroy) + if destroy then + destroyElement(aMuteDetails.Form) + aMuteDetails.Form = nil + else + removeEventHandler("onClientGUIClick", aMuteDetails.Form, aMuteDetails.onClick) + guiSetVisible(aMuteDetails.Form, false) + aMuteDetails.Reset() + end + aMuteDetails.Serial = nil +end + +function aMuteDetails.Create() + local sx, sy = guiGetScreenSize() + aMuteDetails.Form = guiCreateWindow(sx / 2 - 175, sy / 2 - 135, 350, 250, "Mute Details", false) + aMuteDetails.ConfirmationText = guiCreateLabel(25, 40, 300, 20, "Mute details:", false, aMuteDetails.Form) + aMuteDetails.NickText = guiCreateLabel(50, 70, 300, 20, "Player name: Unknown", false, aMuteDetails.Form) + aMuteDetails.SerialText = guiCreateLabel(50, 90, 300, 20, "Serial: None", false, aMuteDetails.Form) + aMuteDetails.ReasonText = guiCreateLabel(50, 110, 300, 20, "Reason: None", false, aMuteDetails.Form) + aMuteDetails.AdminText = guiCreateLabel(50, 130, 300, 20, "Responsible admin: Unknown", false, aMuteDetails.Form) + aMuteDetails.DurationText = guiCreateLabel(50, 150, 300, 20, "Duration: Never", false, aMuteDetails.Form) + + aMuteDetails.SubmitButton = guiCreateButton(105, 200, 60, 40, "Submit", false, aMuteDetails.Form) + aMuteDetails.CancelButton = guiCreateButton(185, 200, 60, 40, "Cancel", false, aMuteDetails.Form) + aMuteDetails.CloseButton = guiCreateButton(145, 200, 60, 40, "Close", false, aMuteDetails.Form) + guiSetVisible(aMuteDetails.CloseButton, true) + guiSetVisible(aMuteDetails.SubmitButton, false) + guiSetVisible(aMuteDetails.CancelButton, false) + aRegister("Mute Details", aMuteDetails.Form, aMuteDetails.Show, aMuteDetails.Close) + guiSetVisible(aMuteDetails.Form, false) +end + +function aMuteDetails.Reset() + guiSetText(aMuteDetails.Form, "Mute Details") + guiSetText(aMuteDetails.ConfirmationText, "Mute details:") + guiSetText(aMuteDetails.NickText, "Player name: Unknown") + guiSetText(aMuteDetails.SerialText, "Serial: None") + guiSetText(aMuteDetails.ReasonText, "Reason: None") + guiSetText(aMuteDetails.AdminText, "Responsible admin: Unknown") + guiSetText(aMuteDetails.DurationText, "Duration: Never") + guiSetVisible(aMuteDetails.CloseButton, true) + guiSetVisible(aMuteDetails.SubmitButton, false) + guiSetVisible(aMuteDetails.CancelButton, false) +end + +function aMuteDetails.onClick(button, state) + if not (button == "left" and state == "up") then + return + end + + -- Handle cancel button first + if source == aMuteDetails.CancelButton or source == aMuteDetails.CloseButton then + aMuteDetails.Close() + return + end + + if source == aMuteDetails.SubmitButton then + triggerServerEvent(EVENT_MUTE, localPlayer, "unmute", {serial = aMuteDetails.Serial}) + aMuteDetails.Close() + sync(SYNC_MUTES) + return + end +end \ No newline at end of file diff --git a/[admin]/admin2/conf/ACL.xml b/[admin]/admin2/conf/ACL.xml index 35d2574d7..5af3fe12b 100644 --- a/[admin]/admin2/conf/ACL.xml +++ b/[admin]/admin2/conf/ACL.xml @@ -14,12 +14,11 @@ + - - @@ -90,6 +89,10 @@ + + + + @@ -101,12 +104,11 @@ + - - @@ -176,6 +178,10 @@ + + + + @@ -187,12 +193,11 @@ + - - @@ -262,6 +267,10 @@ + + + + @@ -273,12 +282,11 @@ + - - @@ -349,5 +357,9 @@ + + + + \ No newline at end of file diff --git a/[admin]/admin2/conf/messages.xml b/[admin]/admin2/conf/messages.xml index 062dd7be5..cf52edd7d 100644 --- a/[admin]/admin2/conf/messages.xml +++ b/[admin]/admin2/conf/messages.xml @@ -15,7 +15,7 @@ ADMIN: $admin has kicked $player ($data) - $player has been muted by $admin. + $player has been muted by $admin ($data). ADMIN: $admin has muted $player diff --git a/[admin]/admin2/meta.xml b/[admin]/admin2/meta.xml index b571c9415..696eaecc1 100644 --- a/[admin]/admin2/meta.xml +++ b/[admin]/admin2/meta.xml @@ -30,7 +30,8 @@