This commit is contained in:
Nordi98 2025-08-04 09:35:37 +02:00
parent 4f8d916728
commit 4d24104e50
6 changed files with 3089 additions and 2383 deletions

View file

@ -984,191 +984,379 @@ function table.count(t)
return count
end
-- Neuer Event-Handler für benutzerdefinierte Lizenzen
RegisterNetEvent('license-system:server:issueCustomLicense', function(targetId, licenseType, customData, classes)
-- Add to server/main.lua
RegisterNetEvent('license-system:server:reactivateLicense', function(targetId, licenseType)
local src = source
local Player = QBCore.Functions.GetPlayer(src)
local TargetPlayer = QBCore.Functions.GetPlayer(targetId)
debugPrint("=== Event: reactivateLicense ===")
debugPrint("Source: " .. src .. ", Target: " .. targetId .. ", Type: " .. licenseType)
if not Player or not TargetPlayer then
debugPrint("Spieler nicht gefunden: " .. src .. " -> " .. targetId)
-- 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
-- Berechtigung prüfen
if not isAuthorized(Player.PlayerData.job.name) then
-- 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
-- Lizenz-Konfiguration prüfen
local config = Config.LicenseTypes[licenseType]
if not config then
debugPrint("Unbekannter Lizenztyp: " .. licenseType)
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
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
local issuerPlayer = QBCore.Functions.GetPlayer(src)
if not issuerPlayer then
debugPrint("Aussteller nicht gefunden: " .. src)
return
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
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
-- Lizenz in Datenbank speichern
local success = saveCustomLicenseToDB(
TargetPlayer.PlayerData.citizenid,
licenseType,
Player.PlayerData.charinfo.firstname .. " " .. Player.PlayerData.charinfo.lastname,
validatedData,
validatedClasses
)
-- 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
if success then
debugPrint("Benutzerdefinierte Lizenz erfolgreich gespeichert")
-- 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)
-- Cache invalidieren
invalidateCache(TargetPlayer.PlayerData.citizenid, licenseType)
local targetName = getPlayerName(targetId)
local issuerName = getPlayerName(src)
local config = Config.LicenseTypes[licenseData.license_type]
-- 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")
-- 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, licenseType)
TriggerClientEvent('license-system:client:licenseIssued', src, targetId, licenseData.license_type)
TriggerClientEvent('license-system:client:refreshMenu', src)
-- Log
debugPrint("Lizenz ausgestellt: " .. licenseType .. " von " .. Player.PlayerData.name .. " an " .. TargetPlayer.PlayerData.name)
debugPrint("Lizenz " .. licenseData.license_type .. " manuell ausgestellt von " .. issuerName .. " für " .. targetName)
else
debugPrint("Fehler beim Speichern der benutzerdefinierten Lizenz")
TriggerClientEvent('QBCore:Notify', src, "Fehler beim Ausstellen der Lizenz", "error")
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
-- 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))
-- 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))
if not hasPermission(src) then
debugPrint("Keine Berechtigung für Spieler: " .. src)
TriggerClientEvent('license-system:client:receiveAllPlayerLicenses', src, {}, targetId, "Unbekannt")
return
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]
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 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")
local citizenid = targetPlayer.PlayerData.citizenid
local targetName = getPlayerName(targetId)
if license then
-- Custom-Data und Classes parsen
if license.custom_data then
license.custom_data_parsed = json.decode(license.custom_data)
-- 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
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
debugPrint("License-System Server erweitert geladen (Custom License Support)")
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)")