forked from Simnation/Main
ed
This commit is contained in:
parent
4d24104e50
commit
05be118a84
2 changed files with 477 additions and 691 deletions
|
@ -6,6 +6,8 @@ local currentTarget = nil
|
||||||
local nearbyPlayers = {}
|
local nearbyPlayers = {}
|
||||||
local isLicenseShowing = false
|
local isLicenseShowing = false
|
||||||
local pendingRequests = {}
|
local pendingRequests = {}
|
||||||
|
local currentLicenseData = nil
|
||||||
|
local currentTargetId = nil
|
||||||
|
|
||||||
-- Hilfsfunktionen
|
-- Hilfsfunktionen
|
||||||
local function debugPrint(message)
|
local function debugPrint(message)
|
||||||
|
@ -116,6 +118,20 @@ local openDriversLicenseClassMenu
|
||||||
local openRevokeLicenseMenu
|
local openRevokeLicenseMenu
|
||||||
local openPlayerLicenseMenu
|
local openPlayerLicenseMenu
|
||||||
local openLicenseMenu
|
local openLicenseMenu
|
||||||
|
local openManualLicenseEntry
|
||||||
|
local openReactivateLicenseMenu
|
||||||
|
|
||||||
|
-- Helper function to get license type options
|
||||||
|
local function getLicenseTypeOptions()
|
||||||
|
local options = {}
|
||||||
|
for licenseType, config in pairs(Config.LicenseTypes) do
|
||||||
|
table.insert(options, {
|
||||||
|
value = licenseType,
|
||||||
|
label = config.label
|
||||||
|
})
|
||||||
|
end
|
||||||
|
return options
|
||||||
|
end
|
||||||
|
|
||||||
-- Lizenz-Ausstellung bestätigen (FRÜH DEFINIERT)
|
-- Lizenz-Ausstellung bestätigen (FRÜH DEFINIERT)
|
||||||
confirmIssueLicense = function(targetId, targetName, licenseType, classes)
|
confirmIssueLicense = function(targetId, targetName, licenseType, classes)
|
||||||
|
@ -409,6 +425,51 @@ openRevokeLicenseMenu = function(targetId, targetName, licenses)
|
||||||
lib.showContext('revoke_license')
|
lib.showContext('revoke_license')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Manual License Entry Menu
|
||||||
|
openManualLicenseEntry = function(targetId, targetName)
|
||||||
|
debugPrint("Öffne manuelle Lizenz-Eingabe für: " .. targetName)
|
||||||
|
|
||||||
|
local input = lib.inputDialog('Lizenz manuell ausstellen', {
|
||||||
|
{type = 'select', label = 'Lizenztyp', options = getLicenseTypeOptions()},
|
||||||
|
{type = 'input', label = 'Name', default = targetName},
|
||||||
|
{type = 'input', label = 'Geburtsdatum', description = 'Format: DD.MM.YYYY'},
|
||||||
|
{type = 'select', label = 'Geschlecht', options = {
|
||||||
|
{value = 'male', label = 'Männlich'},
|
||||||
|
{value = 'female', label = 'Weiblich'},
|
||||||
|
{value = 'other', label = 'Divers'}
|
||||||
|
}},
|
||||||
|
{type = 'date', label = 'Ausstellungsdatum', default = os.date("%Y-%m-%d")},
|
||||||
|
{type = 'date', label = 'Ablaufdatum', default = os.date("%Y-%m-%d", os.time() + 365*24*60*60)},
|
||||||
|
{type = 'checkbox', label = 'Foto aufnehmen?'}
|
||||||
|
})
|
||||||
|
|
||||||
|
if not input then return end
|
||||||
|
|
||||||
|
local licenseType = input[1]
|
||||||
|
local licenseData = {
|
||||||
|
name = input[2],
|
||||||
|
birthday = input[3],
|
||||||
|
gender = input[4],
|
||||||
|
issue_date = os.date("%d.%m.%Y", os.time(os.date("!*t", input[5] / 1000))),
|
||||||
|
expire_date = os.date("%d.%m.%Y", os.time(os.date("!*t", input[6] / 1000))),
|
||||||
|
license_type = licenseType
|
||||||
|
}
|
||||||
|
|
||||||
|
if input[7] then -- Take photo
|
||||||
|
TriggerEvent('license-system:client:openCamera', targetId, licenseData)
|
||||||
|
else
|
||||||
|
TriggerServerEvent('license-system:server:issueManualLicense', targetId, licenseData)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- License Reactivation Menu
|
||||||
|
openReactivateLicenseMenu = function(targetId, targetName)
|
||||||
|
debugPrint("Öffne Lizenz-Reaktivierungs-Menü für: " .. targetName)
|
||||||
|
|
||||||
|
-- Request all licenses including inactive ones
|
||||||
|
TriggerServerEvent('license-system:server:requestAllPlayerLicenses', targetId, true)
|
||||||
|
end
|
||||||
|
|
||||||
-- Spieler-Lizenz-Menü
|
-- Spieler-Lizenz-Menü
|
||||||
openPlayerLicenseMenu = function(targetId, targetName)
|
openPlayerLicenseMenu = function(targetId, targetName)
|
||||||
debugPrint("=== openPlayerLicenseMenu START ===")
|
debugPrint("=== openPlayerLicenseMenu START ===")
|
||||||
|
@ -575,6 +636,15 @@ RegisterNetEvent('license-system:client:receivePlayerLicenses', function(license
|
||||||
end
|
end
|
||||||
})
|
})
|
||||||
|
|
||||||
|
table.insert(menuOptions, {
|
||||||
|
title = 'Lizenz manuell ausstellen',
|
||||||
|
description = 'Lizenz mit benutzerdefinierten Daten ausstellen',
|
||||||
|
icon = 'fas fa-edit',
|
||||||
|
onSelect = function()
|
||||||
|
openManualLicenseEntry(targetId, targetName)
|
||||||
|
end
|
||||||
|
})
|
||||||
|
|
||||||
if licenses and #licenses > 0 then
|
if licenses and #licenses > 0 then
|
||||||
table.insert(menuOptions, {
|
table.insert(menuOptions, {
|
||||||
title = 'Lizenz entziehen',
|
title = 'Lizenz entziehen',
|
||||||
|
@ -586,6 +656,15 @@ RegisterNetEvent('license-system:client:receivePlayerLicenses', function(license
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
table.insert(menuOptions, {
|
||||||
|
title = 'Lizenz reaktivieren',
|
||||||
|
description = 'Eine inaktive Lizenz wieder aktivieren',
|
||||||
|
icon = 'fas fa-redo',
|
||||||
|
onSelect = function()
|
||||||
|
openReactivateLicenseMenu(targetId, targetName)
|
||||||
|
end
|
||||||
|
})
|
||||||
|
|
||||||
table.insert(menuOptions, {
|
table.insert(menuOptions, {
|
||||||
title = '← Zurück',
|
title = '← Zurück',
|
||||||
icon = 'fas fa-arrow-left',
|
icon = 'fas fa-arrow-left',
|
||||||
|
@ -603,6 +682,100 @@ RegisterNetEvent('license-system:client:receivePlayerLicenses', function(license
|
||||||
lib.showContext('player_licenses')
|
lib.showContext('player_licenses')
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
-- EVENT HANDLER: Alle Spieler-Lizenzen (inkl. inaktive) erhalten
|
||||||
|
RegisterNetEvent('license-system:client:receiveAllPlayerLicenses', function(licenses, targetId, targetName)
|
||||||
|
debugPrint("=== Event: receiveAllPlayerLicenses ===")
|
||||||
|
debugPrint("Erhaltene Lizenzen: " .. #licenses)
|
||||||
|
|
||||||
|
local menuOptions = {}
|
||||||
|
|
||||||
|
if licenses and #licenses > 0 then
|
||||||
|
for _, license in ipairs(licenses) do
|
||||||
|
if license.is_active == 0 then -- Only show inactive licenses
|
||||||
|
local licenseConfig = Config.LicenseTypes[license.license_type] or {
|
||||||
|
label = license.license_type,
|
||||||
|
icon = 'fas fa-id-card',
|
||||||
|
color = '#667eea'
|
||||||
|
}
|
||||||
|
|
||||||
|
table.insert(menuOptions, {
|
||||||
|
title = licenseConfig.label .. ' ❌',
|
||||||
|
description = 'Status: Ungültig | Ausgestellt: ' .. (license.issue_date or 'Unbekannt'),
|
||||||
|
icon = licenseConfig.icon,
|
||||||
|
onSelect = function()
|
||||||
|
-- Confirmation dialog
|
||||||
|
lib.registerContext({
|
||||||
|
id = 'confirm_reactivate_license',
|
||||||
|
title = 'Lizenz reaktivieren bestätigen',
|
||||||
|
options = {
|
||||||
|
{
|
||||||
|
title = 'Spieler: ' .. targetName,
|
||||||
|
disabled = true,
|
||||||
|
icon = 'fas fa-user'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title = 'Lizenztyp: ' .. licenseConfig.label,
|
||||||
|
disabled = true,
|
||||||
|
icon = licenseConfig.icon
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title = '─────────────────',
|
||||||
|
disabled = true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title = '✅ Bestätigen',
|
||||||
|
description = 'Lizenz jetzt reaktivieren',
|
||||||
|
icon = 'fas fa-check',
|
||||||
|
onSelect = function()
|
||||||
|
debugPrint("Sende Lizenz-Reaktivierung an Server...")
|
||||||
|
TriggerServerEvent('license-system:server:reactivateLicense', targetId, license.license_type)
|
||||||
|
lib.hideContext()
|
||||||
|
end
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title = '❌ Abbrechen',
|
||||||
|
description = 'Vorgang abbrechen',
|
||||||
|
icon = 'fas fa-times',
|
||||||
|
onSelect = function()
|
||||||
|
openReactivateLicenseMenu(targetId, targetName)
|
||||||
|
end
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
lib.showContext('confirm_reactivate_license')
|
||||||
|
end
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if #menuOptions == 0 then
|
||||||
|
table.insert(menuOptions, {
|
||||||
|
title = 'Keine inaktiven Lizenzen',
|
||||||
|
description = 'Dieser Spieler hat keine inaktiven Lizenzen',
|
||||||
|
icon = 'fas fa-exclamation-triangle',
|
||||||
|
disabled = true
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(menuOptions, {
|
||||||
|
title = '← Zurück',
|
||||||
|
icon = 'fas fa-arrow-left',
|
||||||
|
onSelect = function()
|
||||||
|
openPlayerLicenseMenu(targetId, targetName)
|
||||||
|
end
|
||||||
|
})
|
||||||
|
|
||||||
|
lib.registerContext({
|
||||||
|
id = 'reactivate_license',
|
||||||
|
title = 'Lizenz reaktivieren: ' .. targetName,
|
||||||
|
options = menuOptions
|
||||||
|
})
|
||||||
|
|
||||||
|
lib.showContext('reactivate_license')
|
||||||
|
end)
|
||||||
|
|
||||||
-- EVENT HANDLER: Berechtigung erhalten
|
-- EVENT HANDLER: Berechtigung erhalten
|
||||||
RegisterNetEvent('license-system:client:receivePermission', function(hasAuth, licenseType)
|
RegisterNetEvent('license-system:client:receivePermission', function(hasAuth, licenseType)
|
||||||
debugPrint("=== Event: receivePermission ===")
|
debugPrint("=== Event: receivePermission ===")
|
||||||
|
@ -639,6 +812,19 @@ RegisterNetEvent('license-system:client:licenseRevoked', function(targetId, lice
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
-- EVENT HANDLER: Lizenz erfolgreich reaktiviert
|
||||||
|
RegisterNetEvent('license-system:client:licenseReactivated', function(targetId, licenseType)
|
||||||
|
debugPrint("=== Event: licenseReactivated ===")
|
||||||
|
debugPrint("Lizenz " .. licenseType .. " für Spieler " .. targetId .. " reaktiviert")
|
||||||
|
|
||||||
|
-- Menü aktualisieren
|
||||||
|
if lib.getOpenContextMenu() then
|
||||||
|
lib.hideContext()
|
||||||
|
Wait(100)
|
||||||
|
openLicenseMenu()
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
-- EVENT HANDLER: Lizenz anzeigen (von anderen Spielern)
|
-- EVENT HANDLER: Lizenz anzeigen (von anderen Spielern)
|
||||||
RegisterNetEvent('license-system:client:showLicense', function(targetId)
|
RegisterNetEvent('license-system:client:showLicense', function(targetId)
|
||||||
debugPrint("Event erhalten: showLicense für ID " .. tostring(targetId))
|
debugPrint("Event erhalten: showLicense für ID " .. tostring(targetId))
|
||||||
|
@ -652,10 +838,16 @@ RegisterNetEvent('license-system:client:showMyLicense', function(licenseType)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
-- EVENT HANDLER: Kamera öffnen
|
-- EVENT HANDLER: Kamera öffnen
|
||||||
RegisterNetEvent('license-system:client:openCamera', function()
|
RegisterNetEvent('license-system:client:openCamera', function(targetId, licenseData)
|
||||||
debugPrint("Event erhalten: openCamera")
|
debugPrint("Event erhalten: openCamera für Spieler " .. tostring(targetId))
|
||||||
|
|
||||||
|
-- Store the data temporarily
|
||||||
|
currentLicenseData = licenseData
|
||||||
|
currentTargetId = targetId
|
||||||
|
|
||||||
SendNUIMessage({
|
SendNUIMessage({
|
||||||
action = 'openCamera'
|
action = 'openCamera',
|
||||||
|
citizenid = QBCore.Functions.GetPlayerData().citizenid
|
||||||
})
|
})
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
@ -682,14 +874,22 @@ end)
|
||||||
RegisterNUICallback('savePhoto', function(data, cb)
|
RegisterNUICallback('savePhoto', function(data, cb)
|
||||||
debugPrint("NUI Callback: savePhoto")
|
debugPrint("NUI Callback: savePhoto")
|
||||||
|
|
||||||
if data.photo and data.citizenid then
|
if data.photo and currentLicenseData and currentTargetId then
|
||||||
TriggerServerEvent('license-system:server:savePhoto', data.citizenid, data.photo)
|
-- Add photo to license data
|
||||||
|
currentLicenseData.photo_url = data.photo
|
||||||
|
|
||||||
|
-- Issue the license with the photo
|
||||||
|
TriggerServerEvent('license-system:server:issueManualLicense', currentTargetId, currentLicenseData)
|
||||||
|
|
||||||
|
-- Reset temporary data
|
||||||
|
currentLicenseData = nil
|
||||||
|
currentTargetId = nil
|
||||||
|
|
||||||
if cb and type(cb) == "function" then
|
if cb and type(cb) == "function" then
|
||||||
cb('ok')
|
cb('ok')
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
debugPrint("^1Fehler: Foto-Daten unvollständig^7")
|
debugPrint("^1Fehler: Foto-Daten unvollständig oder keine aktuelle Lizenz-Ausstellung^7")
|
||||||
|
|
||||||
if cb and type(cb) == "function" then
|
if cb and type(cb) == "function" then
|
||||||
cb('error')
|
cb('error')
|
||||||
|
@ -773,6 +973,10 @@ AddEventHandler('onResourceStop', function(resourceName)
|
||||||
lib.hideContext()
|
lib.hideContext()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Reset temporary data
|
||||||
|
currentLicenseData = nil
|
||||||
|
currentTargetId = nil
|
||||||
|
|
||||||
debugPrint("License-System Client gestoppt")
|
debugPrint("License-System Client gestoppt")
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
@ -1157,6 +1361,10 @@ local function cleanup()
|
||||||
-- Pending Requests leeren
|
-- Pending Requests leeren
|
||||||
pendingRequests = {}
|
pendingRequests = {}
|
||||||
|
|
||||||
|
-- Temporary data leeren
|
||||||
|
currentLicenseData = nil
|
||||||
|
currentTargetId = nil
|
||||||
|
|
||||||
-- Animationen stoppen
|
-- Animationen stoppen
|
||||||
local playerPed = PlayerPedId()
|
local playerPed = PlayerPedId()
|
||||||
if DoesEntityExist(playerPed) then
|
if DoesEntityExist(playerPed) then
|
||||||
|
@ -1237,312 +1445,6 @@ RegisterCommand('licensedebug', function()
|
||||||
showNotification('Debug-Informationen in der Konsole ausgegeben!', 'success')
|
showNotification('Debug-Informationen in der Konsole ausgegeben!', 'success')
|
||||||
end, false)
|
end, false)
|
||||||
|
|
||||||
-- Erweiterte Keybind-Behandlung
|
-- Finaler Debug-Output
|
||||||
CreateThread(function()
|
debugPrint("License-System Client vollständig geladen - Alle Funktionen verfügbar")
|
||||||
while true do
|
|
||||||
Wait(0)
|
|
||||||
|
|
||||||
-- ESC-Taste für Lizenz schließen
|
|
||||||
if isLicenseShowing then
|
|
||||||
if IsControlJustPressed(0, 322) then -- ESC
|
|
||||||
closeLicense()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Zusätzliche Hotkeys (falls konfiguriert)
|
|
||||||
if Config.Keybinds and Config.Keybinds.emergency_close then
|
|
||||||
if IsControlJustPressed(0, Config.Keybinds.emergency_close.control) then
|
|
||||||
cleanup()
|
|
||||||
showNotification('Notfall-Schließung aktiviert!', 'info')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Performance-Optimierung
|
|
||||||
if not isLicenseShowing and not lib.getOpenContextMenu() then
|
|
||||||
Wait(500)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
-- Erweiterte Netzwerk-Events mit Retry-Mechanismus
|
|
||||||
local function sendEventWithRetry(eventName, data, maxRetries)
|
|
||||||
maxRetries = maxRetries or 3
|
|
||||||
local retries = 0
|
|
||||||
|
|
||||||
local function attemptSend()
|
|
||||||
retries = retries + 1
|
|
||||||
debugPrint("Sende Event: " .. eventName .. " (Versuch " .. retries .. "/" .. maxRetries .. ")")
|
|
||||||
|
|
||||||
TriggerServerEvent(eventName, table.unpack(data or {}))
|
|
||||||
|
|
||||||
-- Timeout für Response
|
|
||||||
CreateThread(function()
|
|
||||||
Wait(5000) -- 5 Sekunden Timeout
|
|
||||||
|
|
||||||
if retries < maxRetries then
|
|
||||||
debugPrint("^3Timeout für Event " .. eventName .. " - Wiederhole...^7")
|
|
||||||
attemptSend()
|
|
||||||
else
|
|
||||||
debugPrint("^1Maximale Wiederholungen für Event " .. eventName .. " erreicht^7")
|
|
||||||
showNotification('Netzwerk-Fehler! Bitte versuche es später erneut.', 'error')
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
attemptSend()
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Erweiterte Export-Funktionen für andere Resources
|
|
||||||
exports('hasLicense', function(licenseType)
|
|
||||||
-- Diese Funktion kann von anderen Resources verwendet werden
|
|
||||||
local PlayerData = QBCore.Functions.GetPlayerData()
|
|
||||||
if not PlayerData or not PlayerData.citizenid then return false end
|
|
||||||
|
|
||||||
-- Hier würde normalerweise eine Server-Anfrage gemacht werden
|
|
||||||
-- Für jetzt geben wir false zurück
|
|
||||||
return false
|
|
||||||
end)
|
|
||||||
|
|
||||||
exports('showPlayerLicense', function(targetId, licenseType)
|
|
||||||
-- Export für andere Resources um Lizenzen anzuzeigen
|
|
||||||
if licenseType then
|
|
||||||
TriggerServerEvent('license-system:server:requestSpecificLicense', targetId, licenseType)
|
|
||||||
else
|
|
||||||
showPlayerLicense(targetId)
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
exports('openLicenseMenu', function()
|
|
||||||
-- Export für andere Resources um das Lizenz-Menü zu öffnen
|
|
||||||
openLicenseMenu()
|
|
||||||
end)
|
|
||||||
|
|
||||||
|
|
||||||
-- Neuer Event-Handler für benutzerdefinierte Lizenzen
|
|
||||||
RegisterNetEvent('license-system:server:issueCustomLicense', function(targetId, licenseType, customData, classes)
|
|
||||||
local src = source
|
|
||||||
local Player = QBCore.Functions.GetPlayer(src)
|
|
||||||
local TargetPlayer = QBCore.Functions.GetPlayer(targetId)
|
|
||||||
|
|
||||||
if not Player or not TargetPlayer then
|
|
||||||
debugPrint("Spieler nicht gefunden: " .. src .. " -> " .. targetId)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Berechtigung prüfen
|
|
||||||
if not isAuthorized(Player.PlayerData.job.name) then
|
|
||||||
TriggerClientEvent('QBCore:Notify', src, Config.Notifications.no_permission.message, Config.Notifications.no_permission.type)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Lizenz-Konfiguration prüfen
|
|
||||||
local config = Config.LicenseTypes[licenseType]
|
|
||||||
if not config then
|
|
||||||
debugPrint("Unbekannter Lizenztyp: " .. licenseType)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
debugPrint("Erstelle benutzerdefinierte Lizenz: " .. licenseType .. " für " .. TargetPlayer.PlayerData.citizenid)
|
|
||||||
|
|
||||||
-- Benutzerdefinierte Daten validieren und bereinigen
|
|
||||||
local validatedData = {}
|
|
||||||
|
|
||||||
for _, field in ipairs(config.custom_fields or {}) do
|
|
||||||
local value = customData[field.name]
|
|
||||||
|
|
||||||
-- Pflichtfeld-Prüfung
|
|
||||||
if field.required and (not value or value == "") then
|
|
||||||
TriggerClientEvent('QBCore:Notify', src, "Feld '" .. field.label .. "' ist erforderlich", "error")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Wert bereinigen und validieren
|
|
||||||
if value and value ~= "" then
|
|
||||||
value = string.gsub(value, "'", "''") -- SQL-Injection Schutz
|
|
||||||
|
|
||||||
-- Typ-spezifische Validierung
|
|
||||||
if field.type == "url" and not string.match(value, "^https?://") then
|
|
||||||
TriggerClientEvent('QBCore:Notify', src, "Ungültige URL in Feld '" .. field.label .. "'", "error")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
if field.type == "date" and not string.match(value, "^%d%d%.%d%d%.%d%d%d%d$") then
|
|
||||||
TriggerClientEvent('QBCore:Notify', src, "Ungültiges Datum in Feld '" .. field.label .. "'", "error")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
validatedData[field.name] = value
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Klassen validieren
|
|
||||||
local validatedClasses = {}
|
|
||||||
if config.classes and classes then
|
|
||||||
for _, class in ipairs(classes) do
|
|
||||||
if config.classes[class.key] then
|
|
||||||
table.insert(validatedClasses, class)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Lizenz in Datenbank speichern
|
|
||||||
local success = saveCustomLicenseToDB(
|
|
||||||
TargetPlayer.PlayerData.citizenid,
|
|
||||||
licenseType,
|
|
||||||
Player.PlayerData.charinfo.firstname .. " " .. Player.PlayerData.charinfo.lastname,
|
|
||||||
validatedData,
|
|
||||||
validatedClasses
|
|
||||||
)
|
|
||||||
|
|
||||||
if success then
|
|
||||||
debugPrint("Benutzerdefinierte Lizenz erfolgreich gespeichert")
|
|
||||||
|
|
||||||
-- Cache invalidieren
|
|
||||||
invalidateCache(TargetPlayer.PlayerData.citizenid, licenseType)
|
|
||||||
|
|
||||||
-- Benachrichtigungen
|
|
||||||
TriggerClientEvent('QBCore:Notify', src, Config.Notifications.license_issued.message, Config.Notifications.license_issued.type)
|
|
||||||
TriggerClientEvent('QBCore:Notify', targetId, "Du hast eine " .. config.label .. " erhalten!", "success")
|
|
||||||
|
|
||||||
-- Events
|
|
||||||
TriggerClientEvent('license-system:client:licenseIssued', src, targetId, licenseType)
|
|
||||||
TriggerClientEvent('license-system:client:refreshMenu', src)
|
|
||||||
|
|
||||||
-- Log
|
|
||||||
debugPrint("Lizenz ausgestellt: " .. licenseType .. " von " .. Player.PlayerData.name .. " an " .. TargetPlayer.PlayerData.name)
|
|
||||||
else
|
|
||||||
debugPrint("Fehler beim Speichern der benutzerdefinierten Lizenz")
|
|
||||||
TriggerClientEvent('QBCore:Notify', src, "Fehler beim Ausstellen der Lizenz", "error")
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
-- Funktion zum Speichern benutzerdefinierter Lizenzen
|
|
||||||
function saveCustomLicenseToDB(citizenid, licenseType, issuedBy, customData, classes)
|
|
||||||
local config = Config.LicenseTypes[licenseType]
|
|
||||||
if not config then return false end
|
|
||||||
|
|
||||||
-- Ablaufdatum berechnen
|
|
||||||
local expireDate = nil
|
|
||||||
if config.validity_days then
|
|
||||||
expireDate = os.date('%Y-%m-%d %H:%M:%S', os.time() + (config.validity_days * 24 * 60 * 60))
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Holder-Name aus Custom-Data oder Standard
|
|
||||||
local holderName = customData.holder_name or "Unbekannt"
|
|
||||||
|
|
||||||
return safeDBOperation(function()
|
|
||||||
-- Alte Lizenzen deaktivieren
|
|
||||||
local deactivateQuery = "UPDATE player_licenses SET is_active = 0 WHERE citizenid = ? AND license_type = ?"
|
|
||||||
MySQL.query.await(deactivateQuery, {citizenid, licenseType})
|
|
||||||
|
|
||||||
-- Neue Lizenz einfügen
|
|
||||||
local insertQuery = [[
|
|
||||||
INSERT INTO player_licenses
|
|
||||||
(citizenid, license_type, name, issue_date, expire_date, issued_by, is_active, classes, custom_data, holder_name)
|
|
||||||
VALUES (?, ?, ?, NOW(), ?, ?, 1, ?, ?, ?)
|
|
||||||
]]
|
|
||||||
|
|
||||||
local result = MySQL.insert.await(insertQuery, {
|
|
||||||
citizenid,
|
|
||||||
licenseType,
|
|
||||||
config.label,
|
|
||||||
expireDate,
|
|
||||||
issuedBy,
|
|
||||||
json.encode(classes or {}),
|
|
||||||
json.encode(customData or {}),
|
|
||||||
holderName
|
|
||||||
})
|
|
||||||
|
|
||||||
return result and result > 0
|
|
||||||
end, "Benutzerdefinierte Lizenz speichern")
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Erweiterte Lizenz-Abruf-Funktion
|
|
||||||
function getLicenseFromDB(citizenid, licenseType, skipCache)
|
|
||||||
-- Cache prüfen
|
|
||||||
local cacheKey = citizenid .. "_" .. licenseType
|
|
||||||
if not skipCache and licenseCache[cacheKey] then
|
|
||||||
debugPrint("Lizenz aus Cache geladen: " .. licenseType)
|
|
||||||
return licenseCache[cacheKey]
|
|
||||||
end
|
|
||||||
|
|
||||||
local license = safeDBOperation(function()
|
|
||||||
local query = [[
|
|
||||||
SELECT *,
|
|
||||||
CASE
|
|
||||||
WHEN expire_date IS NULL THEN 1
|
|
||||||
WHEN expire_date > NOW() THEN 1
|
|
||||||
ELSE 0
|
|
||||||
END as is_valid
|
|
||||||
FROM player_licenses
|
|
||||||
WHERE citizenid = ? AND license_type = ? AND is_active = 1
|
|
||||||
ORDER BY created_at DESC
|
|
||||||
LIMIT 1
|
|
||||||
]]
|
|
||||||
|
|
||||||
local result = MySQL.query.await(query, {citizenid, licenseType})
|
|
||||||
return result and result[1] or nil
|
|
||||||
end, "Lizenz abrufen")
|
|
||||||
|
|
||||||
if license then
|
|
||||||
-- Custom-Data und Classes parsen
|
|
||||||
if license.custom_data then
|
|
||||||
license.custom_data_parsed = json.decode(license.custom_data)
|
|
||||||
end
|
|
||||||
|
|
||||||
if license.classes then
|
|
||||||
license.classes_parsed = json.decode(license.classes)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Cache speichern
|
|
||||||
licenseCache[cacheKey] = license
|
|
||||||
debugPrint("Lizenz in Cache gespeichert: " .. licenseType)
|
|
||||||
end
|
|
||||||
|
|
||||||
return license
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- Add to client/main.lua
|
|
||||||
RegisterNetEvent('license-system:client:openCamera', function(targetId, licenseData)
|
|
||||||
debugPrint("Event erhalten: openCamera für Spieler " .. tostring(targetId))
|
|
||||||
|
|
||||||
-- Store the data temporarily
|
|
||||||
currentLicenseData = licenseData
|
|
||||||
currentTargetId = targetId
|
|
||||||
|
|
||||||
SendNUIMessage({
|
|
||||||
action = 'openCamera',
|
|
||||||
citizenid = QBCore.Functions.GetPlayerData().citizenid
|
|
||||||
})
|
|
||||||
end)
|
|
||||||
|
|
||||||
-- Enhance the NUI callback for photo saving
|
|
||||||
RegisterNUICallback('savePhoto', function(data, cb)
|
|
||||||
debugPrint("NUI Callback: savePhoto")
|
|
||||||
|
|
||||||
if data.photo and currentLicenseData and currentTargetId then
|
|
||||||
-- Add photo to license data
|
|
||||||
currentLicenseData.photo_url = data.photo
|
|
||||||
|
|
||||||
-- Issue the license with the photo
|
|
||||||
TriggerServerEvent('license-system:server:issueManualLicense', currentTargetId, currentLicenseData)
|
|
||||||
|
|
||||||
-- Reset temporary data
|
|
||||||
currentLicenseData = nil
|
|
||||||
currentTargetId = nil
|
|
||||||
|
|
||||||
if cb and type(cb) == "function" then
|
|
||||||
cb('ok')
|
|
||||||
end
|
|
||||||
else
|
|
||||||
debugPrint("^1Fehler: Foto-Daten unvollständig oder keine aktuelle Lizenz-Ausstellung^7")
|
|
||||||
|
|
||||||
if cb and type(cb) == "function" then
|
|
||||||
cb('error')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
|
|
||||||
debugPrint("License-System Server erweitert geladen (Custom License Support)")
|
|
|
@ -722,11 +722,268 @@ RegisterNetEvent('license-system:server:revokeLicense', function(targetId, licen
|
||||||
debugPrint("Lizenz " .. licenseType .. " entzogen von " .. issuerName .. " für " .. targetName)
|
debugPrint("Lizenz " .. licenseType .. " entzogen von " .. issuerName .. " für " .. targetName)
|
||||||
else
|
else
|
||||||
debugPrint("^1Fehler beim Entziehen der Lizenz^7")
|
debugPrint("^1Fehler beim Entziehen der Lizenz^7")
|
||||||
TriggerClientEvent('QBCore:Notify', src, 'Fehler beim Entziehen der Lizenz!',
|
TriggerClientEvent('QBCore:Notify', src, 'Fehler beim Entziehen der Lizenz!', 'error')
|
||||||
'error')
|
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
-- EVENT HANDLER: Lizenz reaktivieren
|
||||||
|
RegisterNetEvent('license-system:server:reactivateLicense', function(targetId, licenseType)
|
||||||
|
local src = source
|
||||||
|
debugPrint("=== Event: reactivateLicense ===")
|
||||||
|
debugPrint("Source: " .. src .. ", Target: " .. targetId .. ", Type: " .. licenseType)
|
||||||
|
|
||||||
|
-- Check if player has permission to reactivate this license type
|
||||||
|
local Player = QBCore.Functions.GetPlayer(src)
|
||||||
|
if not Player then return end
|
||||||
|
|
||||||
|
local job = Player.PlayerData.job.name
|
||||||
|
local canReactivate = false
|
||||||
|
|
||||||
|
-- Check if job can reactivate this license type
|
||||||
|
if Config.ReactivationPermissions and Config.ReactivationPermissions[job] then
|
||||||
|
for _, allowedType in ipairs(Config.ReactivationPermissions[job]) do
|
||||||
|
if allowedType == licenseType then
|
||||||
|
canReactivate = true
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not canReactivate then
|
||||||
|
TriggerClientEvent('QBCore:Notify', src, 'Du darfst diesen Lizenztyp nicht reaktivieren!', 'error')
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Get target player
|
||||||
|
local targetPlayer = QBCore.Functions.GetPlayer(targetId)
|
||||||
|
if not targetPlayer then
|
||||||
|
TriggerClientEvent('QBCore:Notify', src, 'Spieler nicht gefunden!', 'error')
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local targetCitizenId = targetPlayer.PlayerData.citizenid
|
||||||
|
|
||||||
|
-- Find the most recent license of this type (even if inactive)
|
||||||
|
local query = [[
|
||||||
|
SELECT * FROM player_licenses
|
||||||
|
WHERE citizenid = ? AND license_type = ?
|
||||||
|
ORDER BY created_at DESC
|
||||||
|
LIMIT 1
|
||||||
|
]]
|
||||||
|
|
||||||
|
local result = MySQL.query.await(query, {targetCitizenId, licenseType})
|
||||||
|
|
||||||
|
if not result or #result == 0 then
|
||||||
|
TriggerClientEvent('QBCore:Notify', src, 'Keine Lizenz dieses Typs gefunden!', 'error')
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Reactivate the license
|
||||||
|
local updateQuery = "UPDATE player_licenses SET is_active = 1 WHERE id = ?"
|
||||||
|
local success = MySQL.update.await(updateQuery, {result[1].id})
|
||||||
|
|
||||||
|
if success then
|
||||||
|
-- Invalidate cache
|
||||||
|
invalidateCache(targetCitizenId, licenseType)
|
||||||
|
|
||||||
|
local targetName = getPlayerName(targetId)
|
||||||
|
local issuerName = getPlayerName(src)
|
||||||
|
local config = Config.LicenseTypes[licenseType]
|
||||||
|
|
||||||
|
-- Notifications
|
||||||
|
TriggerClientEvent('QBCore:Notify', src, 'Lizenz erfolgreich reaktiviert für ' .. targetName, 'success')
|
||||||
|
TriggerClientEvent('QBCore:Notify', targetId, 'Deine ' .. (config.label or licenseType) .. ' wurde reaktiviert!', 'success')
|
||||||
|
|
||||||
|
-- Events
|
||||||
|
TriggerClientEvent('license-system:client:licenseReactivated', src, targetId, licenseType)
|
||||||
|
TriggerClientEvent('license-system:client:refreshMenu', src)
|
||||||
|
|
||||||
|
-- Log
|
||||||
|
debugPrint("Lizenz " .. licenseType .. " reaktiviert von " .. issuerName .. " für " .. targetName)
|
||||||
|
else
|
||||||
|
TriggerClientEvent('QBCore:Notify', src, 'Fehler beim Reaktivieren der Lizenz!', 'error')
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- EVENT HANDLER: Manuelle Lizenz ausstellen
|
||||||
|
RegisterNetEvent('license-system:server:issueManualLicense', function(targetId, licenseData)
|
||||||
|
local src = source
|
||||||
|
debugPrint("=== Event: issueManualLicense ===")
|
||||||
|
debugPrint("Source: " .. src .. ", Target: " .. targetId)
|
||||||
|
|
||||||
|
if not hasPermission(src) then
|
||||||
|
debugPrint("Keine Berechtigung für Spieler: " .. src)
|
||||||
|
TriggerClientEvent('QBCore:Notify', src, Config.Notifications.no_permission.message, Config.Notifications.no_permission.type)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local targetPlayer = QBCore.Functions.GetPlayer(targetId)
|
||||||
|
if not targetPlayer then
|
||||||
|
debugPrint("Ziel-Spieler nicht gefunden: " .. targetId)
|
||||||
|
TriggerClientEvent('QBCore:Notify', src, 'Spieler nicht gefunden!', 'error')
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local issuerPlayer = QBCore.Functions.GetPlayer(src)
|
||||||
|
if not issuerPlayer then
|
||||||
|
debugPrint("Aussteller nicht gefunden: " .. src)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local targetCitizenId = targetPlayer.PlayerData.citizenid
|
||||||
|
local issuerCitizenId = issuerPlayer.PlayerData.citizenid
|
||||||
|
|
||||||
|
-- Check if license type is valid
|
||||||
|
if not Config.LicenseTypes[licenseData.license_type] then
|
||||||
|
TriggerClientEvent('QBCore:Notify', src, 'Ungültiger Lizenztyp!', 'error')
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Save photo if provided
|
||||||
|
if licenseData.photo_url then
|
||||||
|
-- Here you would save the photo to your storage system
|
||||||
|
-- For example, to a folder or database
|
||||||
|
-- This is just a placeholder
|
||||||
|
debugPrint("Foto für Lizenz vorhanden")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- First deactivate any existing licenses of this type
|
||||||
|
local deactivateQuery = "UPDATE player_licenses SET is_active = 0 WHERE citizenid = ? AND license_type = ?"
|
||||||
|
MySQL.query.await(deactivateQuery, {targetCitizenId, licenseData.license_type})
|
||||||
|
|
||||||
|
-- Insert into database with manual data
|
||||||
|
local query = [[
|
||||||
|
INSERT INTO player_licenses
|
||||||
|
(citizenid, license_type, name, birthday, gender, issue_date, expire_date, issued_by, is_active, photo_url, created_at)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?)
|
||||||
|
]]
|
||||||
|
|
||||||
|
local createdAt = os.time()
|
||||||
|
|
||||||
|
local insertData = {
|
||||||
|
targetCitizenId,
|
||||||
|
licenseData.license_type,
|
||||||
|
licenseData.name,
|
||||||
|
licenseData.birthday,
|
||||||
|
licenseData.gender,
|
||||||
|
licenseData.issue_date,
|
||||||
|
licenseData.expire_date,
|
||||||
|
issuerCitizenId,
|
||||||
|
licenseData.photo_url or '',
|
||||||
|
createdAt
|
||||||
|
}
|
||||||
|
|
||||||
|
local result = MySQL.insert.await(query, insertData)
|
||||||
|
|
||||||
|
if result then
|
||||||
|
-- Invalidate cache
|
||||||
|
invalidateCache(targetCitizenId)
|
||||||
|
|
||||||
|
local targetName = getPlayerName(targetId)
|
||||||
|
local issuerName = getPlayerName(src)
|
||||||
|
local config = Config.LicenseTypes[licenseData.license_type]
|
||||||
|
|
||||||
|
-- Notifications
|
||||||
|
TriggerClientEvent('QBCore:Notify', src, 'Lizenz erfolgreich ausgestellt für ' .. targetName, 'success')
|
||||||
|
TriggerClientEvent('QBCore:Notify', targetId, 'Du hast eine neue Lizenz erhalten: ' .. config.label, 'success')
|
||||||
|
|
||||||
|
-- Events
|
||||||
|
TriggerClientEvent('license-system:client:licenseIssued', src, targetId, licenseData.license_type)
|
||||||
|
TriggerClientEvent('license-system:client:refreshMenu', src)
|
||||||
|
|
||||||
|
-- Log
|
||||||
|
debugPrint("Lizenz " .. licenseData.license_type .. " manuell ausgestellt von " .. issuerName .. " für " .. targetName)
|
||||||
|
else
|
||||||
|
TriggerClientEvent('QBCore:Notify', src, 'Fehler beim Ausstellen der Lizenz!', 'error')
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- EVENT HANDLER: Alle Lizenzen eines Spielers anfordern (inkl. inaktive)
|
||||||
|
RegisterNetEvent('license-system:server:requestAllPlayerLicenses', function(targetId, includeInactive)
|
||||||
|
local src = source
|
||||||
|
debugPrint("=== Event: requestAllPlayerLicenses ===")
|
||||||
|
debugPrint("Source: " .. src .. ", Target: " .. targetId .. ", IncludeInactive: " .. tostring(includeInactive))
|
||||||
|
|
||||||
|
if not hasPermission(src) then
|
||||||
|
debugPrint("Keine Berechtigung für Spieler: " .. src)
|
||||||
|
TriggerClientEvent('license-system:client:receiveAllPlayerLicenses', src, {}, targetId, "Unbekannt")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local targetPlayer = QBCore.Functions.GetPlayer(targetId)
|
||||||
|
if not targetPlayer then
|
||||||
|
debugPrint("Ziel-Spieler nicht gefunden: " .. targetId)
|
||||||
|
TriggerClientEvent('license-system:client:receiveAllPlayerLicenses', src, {}, targetId, "Unbekannt")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local citizenid = targetPlayer.PlayerData.citizenid
|
||||||
|
local targetName = getPlayerName(targetId)
|
||||||
|
|
||||||
|
-- Query to get all licenses, including inactive ones if requested
|
||||||
|
local query = "SELECT * FROM player_licenses WHERE citizenid = ? ORDER BY license_type, created_at DESC"
|
||||||
|
|
||||||
|
local result = MySQL.query.await(query, {citizenid})
|
||||||
|
|
||||||
|
if not result or #result == 0 then
|
||||||
|
debugPrint("Keine Lizenzen gefunden für: " .. citizenid)
|
||||||
|
TriggerClientEvent('license-system:client:receiveAllPlayerLicenses', src, {}, targetId, targetName)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Process licenses
|
||||||
|
local licenses = {}
|
||||||
|
local seenTypes = {}
|
||||||
|
|
||||||
|
for _, license in ipairs(result) do
|
||||||
|
-- If includeInactive is true, add all licenses
|
||||||
|
-- Otherwise, only add the newest license per type
|
||||||
|
local shouldAdd = includeInactive or not seenTypes[license.license_type]
|
||||||
|
if shouldAdd then
|
||||||
|
seenTypes[license.license_type] = true
|
||||||
|
|
||||||
|
-- Add holder name
|
||||||
|
license.holder_name = targetName
|
||||||
|
|
||||||
|
-- Add issuer name
|
||||||
|
if license.issued_by then
|
||||||
|
local issuerQuery = "SELECT charinfo FROM players WHERE citizenid = ?"
|
||||||
|
local issuerResult = MySQL.query.await(issuerQuery, {license.issued_by})
|
||||||
|
|
||||||
|
if issuerResult and #issuerResult > 0 then
|
||||||
|
license.issued_by_name = extractPlayerName(issuerResult[1].charinfo)
|
||||||
|
else
|
||||||
|
license.issued_by_name = "System"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
license.issued_by_name = "System"
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Parse classes
|
||||||
|
if license.classes then
|
||||||
|
local success, classes = pcall(json.decode, license.classes)
|
||||||
|
if success and type(classes) == "table" then
|
||||||
|
license.classes = classes
|
||||||
|
else
|
||||||
|
license.classes = {}
|
||||||
|
end
|
||||||
|
else
|
||||||
|
license.classes = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Normalize is_active
|
||||||
|
if license.is_active == nil then
|
||||||
|
license.is_active = 1
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(licenses, license)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
debugPrint("Sende " .. #licenses .. " Lizenzen für " .. targetName)
|
||||||
|
TriggerClientEvent('license-system:client:receiveAllPlayerLicenses', src, licenses, targetId, targetName)
|
||||||
|
end)
|
||||||
|
|
||||||
-- EXPORT FUNKTIONEN (KORRIGIERT)
|
-- EXPORT FUNKTIONEN (KORRIGIERT)
|
||||||
exports('hasLicense', function(citizenid, licenseType)
|
exports('hasLicense', function(citizenid, licenseType)
|
||||||
if not citizenid or not licenseType then return false end
|
if not citizenid or not licenseType then return false end
|
||||||
|
@ -984,379 +1241,6 @@ function table.count(t)
|
||||||
return count
|
return count
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Add to server/main.lua
|
|
||||||
RegisterNetEvent('license-system:server:reactivateLicense', function(targetId, licenseType)
|
|
||||||
local src = source
|
|
||||||
debugPrint("=== Event: reactivateLicense ===")
|
|
||||||
debugPrint("Source: " .. src .. ", Target: " .. targetId .. ", Type: " .. licenseType)
|
|
||||||
|
|
||||||
-- Check if player has permission to reactivate this license type
|
|
||||||
local Player = QBCore.Functions.GetPlayer(src)
|
|
||||||
if not Player then return end
|
|
||||||
|
|
||||||
local job = Player.PlayerData.job.name
|
|
||||||
local canReactivate = false
|
|
||||||
|
|
||||||
-- Check if job can reactivate this license type
|
|
||||||
if Config.ReactivationPermissions and Config.ReactivationPermissions[job] then
|
|
||||||
for _, allowedType in ipairs(Config.ReactivationPermissions[job]) do
|
|
||||||
if allowedType == licenseType then
|
|
||||||
canReactivate = true
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if not canReactivate then
|
|
||||||
TriggerClientEvent('QBCore:Notify', src, 'Du darfst diesen Lizenztyp nicht reaktivieren!', 'error')
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Get target player
|
|
||||||
local targetPlayer = QBCore.Functions.GetPlayer(targetId)
|
|
||||||
if not targetPlayer then
|
|
||||||
TriggerClientEvent('QBCore:Notify', src, 'Spieler nicht gefunden!', 'error')
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local targetCitizenId = targetPlayer.PlayerData.citizenid
|
|
||||||
|
|
||||||
-- Find the most recent license of this type (even if inactive)
|
|
||||||
local query = [[
|
|
||||||
SELECT * FROM player_licenses
|
|
||||||
WHERE citizenid = ? AND license_type = ?
|
|
||||||
ORDER BY created_at DESC
|
|
||||||
LIMIT 1
|
|
||||||
]]
|
|
||||||
|
|
||||||
local result = MySQL.query.await(query, {targetCitizenId, licenseType})
|
|
||||||
|
|
||||||
if not result or #result == 0 then
|
|
||||||
TriggerClientEvent('QBCore:Notify', src, 'Keine Lizenz dieses Typs gefunden!', 'error')
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Reactivate the license
|
|
||||||
local updateQuery = "UPDATE player_licenses SET is_active = 1 WHERE id = ?"
|
|
||||||
local success = MySQL.update.await(updateQuery, {result[1].id})
|
|
||||||
|
|
||||||
if success then
|
|
||||||
-- Invalidate cache
|
|
||||||
invalidateCache(targetCitizenId, licenseType)
|
|
||||||
|
|
||||||
local targetName = getPlayerName(targetId)
|
|
||||||
local issuerName = getPlayerName(src)
|
|
||||||
local config = Config.LicenseTypes[licenseType]
|
|
||||||
|
|
||||||
-- Notifications
|
|
||||||
TriggerClientEvent('QBCore:Notify', src, 'Lizenz erfolgreich reaktiviert für ' .. targetName, 'success')
|
|
||||||
TriggerClientEvent('QBCore:Notify', targetId, 'Deine ' .. (config.label or licenseType) .. ' wurde reaktiviert!', 'success')
|
|
||||||
|
|
||||||
-- Events
|
|
||||||
TriggerClientEvent('license-system:client:licenseReactivated', src, targetId, licenseType)
|
|
||||||
TriggerClientEvent('license-system:client:refreshMenu', src)
|
|
||||||
|
|
||||||
-- Log
|
|
||||||
debugPrint("Lizenz " .. licenseType .. " reaktiviert von " .. issuerName .. " für " .. targetName)
|
|
||||||
else
|
|
||||||
TriggerClientEvent('QBCore:Notify', src, 'Fehler beim Reaktivieren der Lizenz!', 'error')
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
-- Add to client/main.lua
|
|
||||||
-- Add a reactivation option to the player license menu
|
|
||||||
local function openReactivateLicenseMenu(targetId, targetName)
|
|
||||||
debugPrint("Öffne Lizenz-Reaktivierungs-Menü für: " .. targetName)
|
|
||||||
|
|
||||||
-- Request all licenses including inactive ones
|
|
||||||
TriggerServerEvent('license-system:server:requestAllPlayerLicenses', targetId, true)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- New event to receive all licenses including inactive ones
|
|
||||||
RegisterNetEvent('license-system:client:receiveAllPlayerLicenses', function(licenses, targetId, targetName)
|
|
||||||
debugPrint("=== Event: receiveAllPlayerLicenses ===")
|
|
||||||
debugPrint("Erhaltene Lizenzen: " .. #licenses)
|
|
||||||
|
|
||||||
local menuOptions = {}
|
|
||||||
|
|
||||||
if licenses and #licenses > 0 then
|
|
||||||
for _, license in ipairs(licenses) do
|
|
||||||
if license.is_active == 0 then -- Only show inactive licenses
|
|
||||||
local licenseConfig = Config.LicenseTypes[license.license_type] or {
|
|
||||||
label = license.license_type,
|
|
||||||
icon = 'fas fa-id-card',
|
|
||||||
color = '#667eea'
|
|
||||||
}
|
|
||||||
|
|
||||||
table.insert(menuOptions, {
|
|
||||||
title = licenseConfig.label .. ' ❌',
|
|
||||||
description = 'Status: Ungültig | Ausgestellt: ' .. (license.issue_date or 'Unbekannt'),
|
|
||||||
icon = licenseConfig.icon,
|
|
||||||
onSelect = function()
|
|
||||||
-- Confirmation dialog
|
|
||||||
lib.registerContext({
|
|
||||||
id = 'confirm_reactivate_license',
|
|
||||||
title = 'Lizenz reaktivieren bestätigen',
|
|
||||||
options = {
|
|
||||||
{
|
|
||||||
title = 'Spieler: ' .. targetName,
|
|
||||||
disabled = true,
|
|
||||||
icon = 'fas fa-user'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title = 'Lizenztyp: ' .. licenseConfig.label,
|
|
||||||
disabled = true,
|
|
||||||
icon = licenseConfig.icon
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title = '─────────────────',
|
|
||||||
disabled = true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title = '✅ Bestätigen',
|
|
||||||
description = 'Lizenz jetzt reaktivieren',
|
|
||||||
icon = 'fas fa-check',
|
|
||||||
onSelect = function()
|
|
||||||
debugPrint("Sende Lizenz-Reaktivierung an Server...")
|
|
||||||
TriggerServerEvent('license-system:server:reactivateLicense', targetId, license.license_type)
|
|
||||||
lib.hideContext()
|
|
||||||
end
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title = '❌ Abbrechen',
|
|
||||||
description = 'Vorgang abbrechen',
|
|
||||||
icon = 'fas fa-times',
|
|
||||||
onSelect = function()
|
|
||||||
openReactivateLicenseMenu(targetId, targetName)
|
|
||||||
end
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
lib.showContext('confirm_reactivate_license')
|
|
||||||
end
|
|
||||||
})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if #menuOptions == 0 then
|
|
||||||
table.insert(menuOptions, {
|
|
||||||
title = 'Keine inaktiven Lizenzen',
|
|
||||||
description = 'Dieser Spieler hat keine inaktiven Lizenzen',
|
|
||||||
icon = 'fas fa-exclamation-triangle',
|
|
||||||
disabled = true
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
table.insert(menuOptions, {
|
|
||||||
title = '← Zurück',
|
|
||||||
icon = 'fas fa-arrow-left',
|
|
||||||
onSelect = function()
|
|
||||||
openPlayerLicenseMenu(targetId, targetName)
|
|
||||||
end
|
|
||||||
})
|
|
||||||
|
|
||||||
lib.registerContext({
|
|
||||||
id = 'reactivate_license',
|
|
||||||
title = 'Lizenz reaktivieren: ' .. targetName,
|
|
||||||
options = menuOptions
|
|
||||||
})
|
|
||||||
|
|
||||||
lib.showContext('reactivate_license')
|
|
||||||
end)
|
|
||||||
|
|
||||||
-- Add this option to the player license menu
|
|
||||||
-- In openPlayerLicenseMenu function, add:
|
|
||||||
table.insert(menuOptions, {
|
|
||||||
title = 'Lizenz reaktivieren',
|
|
||||||
description = 'Eine inaktive Lizenz wieder aktivieren',
|
|
||||||
icon = 'fas fa-redo',
|
|
||||||
onSelect = function()
|
|
||||||
openReactivateLicenseMenu(targetId, targetName)
|
|
||||||
end
|
|
||||||
})
|
|
||||||
|
|
||||||
-- Add to server/main.lua
|
|
||||||
RegisterNetEvent('license-system:server:issueManualLicense', function(targetId, licenseData)
|
|
||||||
local src = source
|
|
||||||
debugPrint("=== Event: issueManualLicense ===")
|
|
||||||
debugPrint("Source: " .. src .. ", Target: " .. targetId)
|
|
||||||
|
|
||||||
if not hasPermission(src) then
|
|
||||||
debugPrint("Keine Berechtigung für Spieler: " .. src)
|
|
||||||
TriggerClientEvent('QBCore:Notify', src, Config.Notifications.no_permission.message, Config.Notifications.no_permission.type)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local targetPlayer = QBCore.Functions.GetPlayer(targetId)
|
|
||||||
if not targetPlayer then
|
|
||||||
debugPrint("Ziel-Spieler nicht gefunden: " .. targetId)
|
|
||||||
TriggerClientEvent('QBCore:Notify', src, 'Spieler nicht gefunden!', 'error')
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local issuerPlayer = QBCore.Functions.GetPlayer(src)
|
|
||||||
if not issuerPlayer then
|
|
||||||
debugPrint("Aussteller nicht gefunden: " .. src)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local targetCitizenId = targetPlayer.PlayerData.citizenid
|
|
||||||
local issuerCitizenId = issuerPlayer.PlayerData.citizenid
|
|
||||||
|
|
||||||
-- Check if license type is valid
|
|
||||||
if not Config.LicenseTypes[licenseData.license_type] then
|
|
||||||
TriggerClientEvent('QBCore:Notify', src, 'Ungültiger Lizenztyp!', 'error')
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Save photo if provided
|
|
||||||
if licenseData.photo_url then
|
|
||||||
-- Here you would save the photo to your storage system
|
|
||||||
-- For example, to a folder or database
|
|
||||||
-- This is just a placeholder
|
|
||||||
debugPrint("Foto für Lizenz vorhanden")
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Insert into database with manual data
|
|
||||||
local query = [[
|
|
||||||
INSERT INTO player_licenses
|
|
||||||
(citizenid, license_type, name, birthday, gender, issue_date, expire_date, issued_by, is_active, photo_url, created_at)
|
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?)
|
|
||||||
]]
|
|
||||||
|
|
||||||
local createdAt = os.time()
|
|
||||||
|
|
||||||
local insertData = {
|
|
||||||
targetCitizenId,
|
|
||||||
licenseData.license_type,
|
|
||||||
licenseData.name,
|
|
||||||
licenseData.birthday,
|
|
||||||
licenseData.gender,
|
|
||||||
licenseData.issue_date,
|
|
||||||
licenseData.expire_date,
|
|
||||||
issuerCitizenId,
|
|
||||||
licenseData.photo_url or '',
|
|
||||||
createdAt
|
|
||||||
}
|
|
||||||
|
|
||||||
-- First deactivate any existing licenses of this type
|
|
||||||
local deactivateQuery = "UPDATE player_licenses SET is_active = 0 WHERE citizenid = ? AND license_type = ?"
|
|
||||||
MySQL.query.await(deactivateQuery, {targetCitizenId, licenseData.license_type})
|
|
||||||
|
|
||||||
-- Then insert the new license
|
|
||||||
local result = MySQL.insert.await(query, insertData)
|
|
||||||
|
|
||||||
if result then
|
|
||||||
-- Invalidate cache
|
|
||||||
invalidateCache(targetCitizenId)
|
|
||||||
|
|
||||||
local targetName = getPlayerName(targetId)
|
|
||||||
local issuerName = getPlayerName(src)
|
|
||||||
local config = Config.LicenseTypes[licenseData.license_type]
|
|
||||||
|
|
||||||
-- Notifications
|
|
||||||
TriggerClientEvent('QBCore:Notify', src, 'Lizenz erfolgreich ausgestellt für ' .. targetName, 'success')
|
|
||||||
TriggerClientEvent('QBCore:Notify', targetId, 'Du hast eine neue Lizenz erhalten: ' .. config.label, 'success')
|
|
||||||
|
|
||||||
-- Events
|
|
||||||
TriggerClientEvent('license-system:client:licenseIssued', src, targetId, licenseData.license_type)
|
|
||||||
TriggerClientEvent('license-system:client:refreshMenu', src)
|
|
||||||
|
|
||||||
-- Log
|
|
||||||
debugPrint("Lizenz " .. licenseData.license_type .. " manuell ausgestellt von " .. issuerName .. " für " .. targetName)
|
|
||||||
else
|
|
||||||
TriggerClientEvent('QBCore:Notify', src, 'Fehler beim Ausstellen der Lizenz!', 'error')
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
-- Add a new event to get all licenses including inactive ones
|
|
||||||
RegisterNetEvent('license-system:server:requestAllPlayerLicenses', function(targetId, includeInactive)
|
|
||||||
local src = source
|
|
||||||
debugPrint("=== Event: requestAllPlayerLicenses ===")
|
|
||||||
debugPrint("Source: " .. src .. ", Target: " .. targetId .. ", IncludeInactive: " .. tostring(includeInactive))
|
|
||||||
|
|
||||||
if not hasPermission(src) then
|
|
||||||
debugPrint("Keine Berechtigung für Spieler: " .. src)
|
|
||||||
TriggerClientEvent('license-system:client:receiveAllPlayerLicenses', src, {}, targetId, "Unbekannt")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local targetPlayer = QBCore.Functions.GetPlayer(targetId)
|
|
||||||
if not targetPlayer then
|
|
||||||
debugPrint("Ziel-Spieler nicht gefunden: " .. targetId)
|
|
||||||
TriggerClientEvent('license-system:client:receiveAllPlayerLicenses', src, {}, targetId, "Unbekannt")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local citizenid = targetPlayer.PlayerData.citizenid
|
|
||||||
local targetName = getPlayerName(targetId)
|
|
||||||
|
|
||||||
-- Query to get all licenses, including inactive ones if requested
|
|
||||||
local query = "SELECT * FROM player_licenses WHERE citizenid = ? ORDER BY license_type, created_at DESC"
|
|
||||||
|
|
||||||
local result = MySQL.query.await(query, {citizenid})
|
|
||||||
|
|
||||||
if not result or #result == 0 then
|
|
||||||
debugPrint("Keine Lizenzen gefunden für: " .. citizenid)
|
|
||||||
TriggerClientEvent('license-system:client:receiveAllPlayerLicenses', src, {}, targetId, targetName)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Process licenses
|
|
||||||
local licenses = {}
|
|
||||||
local seenTypes = {}
|
|
||||||
|
|
||||||
for _, license in ipairs(result) do
|
|
||||||
-- If includeInactive is true, add all licenses
|
|
||||||
-- Otherwise, only add the newest license per type
|
|
||||||
local shouldAdd = includeInactive or not seenTypes[license.license_type]
|
|
||||||
|
|
||||||
if shouldAdd then
|
|
||||||
seenTypes[license.license_type] = true
|
|
||||||
|
|
||||||
-- Add holder name
|
|
||||||
license.holder_name = targetName
|
|
||||||
|
|
||||||
-- Add issuer name
|
|
||||||
if license.issued_by then
|
|
||||||
local issuerQuery = "SELECT charinfo FROM players WHERE citizenid = ?"
|
|
||||||
local issuerResult = MySQL.query.await(issuerQuery, {license.issued_by})
|
|
||||||
|
|
||||||
if issuerResult and #issuerResult > 0 then
|
|
||||||
license.issued_by_name = extractPlayerName(issuerResult[1].charinfo)
|
|
||||||
else
|
|
||||||
license.issued_by_name = "System"
|
|
||||||
end
|
|
||||||
else
|
|
||||||
license.issued_by_name = "System"
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Parse classes
|
|
||||||
if license.classes then
|
|
||||||
local success, classes = pcall(json.decode, license.classes)
|
|
||||||
if success and type(classes) == "table" then
|
|
||||||
license.classes = classes
|
|
||||||
else
|
|
||||||
license.classes = {}
|
|
||||||
end
|
|
||||||
else
|
|
||||||
license.classes = {}
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Normalize is_active
|
|
||||||
if license.is_active == nil then
|
|
||||||
license.is_active = 1
|
|
||||||
end
|
|
||||||
|
|
||||||
table.insert(licenses, license)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
debugPrint("Sende " .. #licenses .. " Lizenzen für " .. targetName)
|
|
||||||
TriggerClientEvent('license-system:client:receiveAllPlayerLicenses', src, licenses, targetId, targetName)
|
|
||||||
end)
|
|
||||||
|
|
||||||
|
|
||||||
debugPrint("License-System Server vollständig geladen (Status-Fix)")
|
debugPrint("License-System Server vollständig geladen (Status-Fix)")
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue