-- ───────────────────────────────────────────────────────────────────────────── -- Kalender -- -- Drei Arten von Terminen: -- -- privat gehoert einer Person, sonst sieht ihn niemand -- geteilt gehoert einem Postfach, sichtbar fuer alle, die es bedienen -- oeffentlich gehoert einem Postfach, sichtbar fuer jeden auf dem Server -- -- Geteilt wird ueber dasselbe Mittel wie Postfaecher und Ordner: die Adresse. -- Ein oeffentlicher Termin haengt bewusst auch an einem Postfach – sonst gaebe -- es niemanden, der ihn spaeter aendern oder absagen koennte. -- ───────────────────────────────────────────────────────────────────────────── --- Alle Postfaecher, die diese Person bedienen darf. ---@return string[] local function MailboxesOf(identifier) if GetResourceState('ic-mail') ~= 'started' then return {} end local out = {} for _, mb in ipairs(exports['ic-mail']:GetAddressesForIdentifier(identifier) or {}) do if mb.address then out[#out + 1] = mb.address end end return out end local function CanUseMailbox(identifier, address) if address == '' then return true end for _, addr in ipairs(MailboxesOf(identifier)) do if addr == address then return true end end return false end --- Darf diese Person den Termin aendern? --- Privat: nur wer ihn angelegt hat. Postfach: wer das Postfach bedient – --- auch wenn jemand anderes ihn eingetragen hat. Genau das macht ihn geteilt. local function CanEditEvent(identifier, event) if not event then return false end if event.address and event.address ~= '' then return CanUseMailbox(identifier, event.address) end return event.identifier == identifier end --- Eingaben pruefen und in die Form bringen, die die Datenbank erwartet. --- Gibt (daten, adresse, sichtbarkeit) oder nil zurueck. local function ReadEvent(identifier, data) if type(data) ~= 'table' then return nil end local d = { title = Utils.Sanitize(tostring(data.title or "")):sub(1, 120), description = Utils.Sanitize(tostring(data.description or "")):sub(1, 500), start_at = Utils.Sanitize(tostring(data.start_at or "")):sub(1, 20), end_at = data.end_at and Utils.Sanitize(tostring(data.end_at)):sub(1, 20) or nil, color = Utils.Sanitize(tostring(data.color or "#00aaff")):sub(1, 20), } if #d.title < 1 or #d.start_at < 1 then return nil end local address = Utils.Sanitize(tostring(data.address or '')):sub(1, 200) if not CanUseMailbox(identifier, address) then -- Kein Zugriff auf das Postfach: der Termin wird privat, statt in einem -- fremden Kalender zu landen. address = '' end local visibility = tostring(data.visibility or 'private') if address == '' then -- Ohne Postfach gibt es niemanden, der einen oeffentlichen Termin -- pflegen koennte. Also bleibt er privat. visibility = 'private' elseif visibility ~= 'public' then visibility = 'shared' end return d, address, visibility end --- Termin so aufbereiten, wie ihn der Client braucht. local function PublicEvent(identifier, row) row.can_edit = CanEditEvent(identifier, row) row.address = row.address or '' return row end local function SendCalendar(src, identifier) local rows = DB.GetCalendar(identifier, MailboxesOf(identifier)) for _, row in ipairs(rows) do PublicEvent(identifier, row) end TriggerClientEvent('pc-live:client:calendarData', src, rows) end --- Alle, die einen Termin sehen, bekommen die Aenderung sofort. --- Ohne das haette jeder eine andere Vorstellung vom Dienstplan, bis er den --- PC neu oeffnet – und genau dafuer ist ein geteilter Kalender da. local function NotifyOthers(src, event) if not event then return end local shared = (event.address and event.address ~= '') or event.visibility == 'public' if not shared then return end for playerSrc, session in pairs(ActiveSessions) do if playerSrc ~= src then if event.visibility == 'public' or CanUseMailbox(session.identifier, event.address or '') then SendCalendar(playerSrc, session.identifier) end end end end -- ── Lesen ──────────────────────────────────────────────────────────────────── RegisterNetEvent('pc-live:server:getCalendar', function() local src = source local user = ActiveSessions[src] if not user then return end SendCalendar(src, user.identifier) end) -- ── Anlegen ────────────────────────────────────────────────────────────────── RegisterNetEvent('pc-live:server:addCalendarEvent', function(data) local src = source local user = ActiveSessions[src] if not user then return end local d, address, visibility = ReadEvent(user.identifier, data) if not d then return end local id = DB.AddCalendarEvent(user.identifier, address, visibility, d) d.id = id d.address = address d.visibility = visibility d.identifier = user.identifier d.can_edit = true TriggerClientEvent('pc-live:client:calendarEventAdded', src, d) NotifyOthers(src, d) end) -- ── Aendern ────────────────────────────────────────────────────────────────── RegisterNetEvent('pc-live:server:updateCalendarEvent', function(id, data) local src = source local user = ActiveSessions[src] if not user then return end if type(id) ~= "number" then return end -- Erst der bestehende Termin: sonst koennte man einen fremden aendern, -- indem man einfach eine Adresse mitschickt, auf die man Zugriff hat. local existing = DB.GetCalendarEvent(id) if not CanEditEvent(user.identifier, existing) then return end local d, address, visibility = ReadEvent(user.identifier, data) if not d then return end DB.UpdateCalendarEvent(id, address, visibility, d) d.id = id d.address = address d.visibility = visibility d.can_edit = true TriggerClientEvent('pc-live:client:calendarEventUpdated', src, d) -- Beide Seiten benachrichtigen: der Termin kann aus einem geteilten -- Kalender herausgenommen worden sein, dann muss er dort verschwinden. NotifyOthers(src, existing) NotifyOthers(src, d) end) -- ── Loeschen ───────────────────────────────────────────────────────────────── RegisterNetEvent('pc-live:server:deleteCalendarEvent', function(id) local src = source local user = ActiveSessions[src] if not user then return end if type(id) ~= "number" then return end local existing = DB.GetCalendarEvent(id) if not CanEditEvent(user.identifier, existing) then return end DB.DeleteCalendarEvent(id) TriggerClientEvent('pc-live:client:calendarEventDeleted', src, id) NotifyOthers(src, existing) end)