-- ===================================================================== -- aether-medical / Modul: Nachrichten und Aushänge -- -- Ein Postfach und ein Schwarzes Brett. -- -- Was hier steht, hat jemand geschrieben und mit seinem Namen -- unterschrieben. Deshalb gilt durchgehend: -- -- * Eine abgeschickte Nachricht wird nicht mehr geändert und nicht -- gelöscht. Sie ist raus. -- * Ein Aushang wird archiviert, nie entfernt — auch ein -- zurückgenommener bleibt nachlesbar. -- * Eine Kenntnisnahme bestätigt nur, dass jemand gelesen hat. -- Niemand kann sie für einen anderen setzen, und sie ändert -- nichts ausser sich selbst. -- * Eine Frist läuft ab, ohne dass etwas geschieht. Das System -- erinnert daran und wartet. -- ===================================================================== -- Lesen darf jeder im Dienst: was gelesen werden muss, soll nicht an -- einem Recht scheitern. Geschrieben wird nur mit Recht. Kern.RegistriereModul({ id = 'kommunikation', name = 'Nachrichten', icon = '✉', reihenfolge = 13, recht = nil, beschreibung = 'Postfach, Rundmeldungen und Schwarzes Brett', }) -- --------------------------------------------------------------------- -- Hilfsfunktionen -- --------------------------------------------------------------------- --- Begrenzt einen Text und liefert nil bei leerer Eingabe --- @param wert any Eingabe --- @param laenge number Maximale Zeichenzahl --- @return string|nil local function Text(wert, laenge) if type(wert) ~= 'string' then return nil end local sauber = wert:gsub('^%s+', ''):gsub('%s+$', '') if sauber == '' then return nil end return sauber:sub(1, laenge) end --- Name eines Charakters für die Anzeige --- @param charakterId number|nil Charakter --- @return string local function NameVon(charakterId) if not charakterId then return 'unbekannt' end local zeilen = MySQL.query.await( 'SELECT firstname, lastname FROM aether_characters WHERE id = ?', { charakterId }) if not zeilen or not zeilen[1] then return 'unbekannt' end return zeilen[1].firstname .. ' ' .. zeilen[1].lastname end --- Anzeigename einer Station aus der Konfiguration --- @param kennung string|nil Stationskennung --- @return string local function StationName(kennung) if not kennung then return 'gesamte Klinik' end for _, station in ipairs(Medizin.Config.Stationen) do if station.id == kennung then return station.name end end return tostring(kennung) end --- Prüft eine Kennung gegen eine Konfigurationsliste --- @param liste table Liste aus Medizin.Config --- @param wert any Eingabe --- @return string|nil local function AusListe(liste, wert) local gesucht = tostring(wert or '') for _, eintrag in ipairs(liste) do if eintrag.id == gesucht then return eintrag.id end end return nil end --- Beschreibt, an wen eine Nachricht ging --- @param eintrag table Nachrichtenzeile --- @return string local function EmpfaengerName(eintrag) if eintrag.empf_typ == 'rundmeldung' then return 'Alle im Haus' end if eintrag.empf_typ == 'station' then return 'Station ' .. StationName(eintrag.empfaenger) end return NameVon(tonumber(eintrag.empfaenger)) end --- Kürzt einen Text für die Listenansicht --- @param wert string|nil Volltext --- @param laenge number Zeichen --- @return string local function Vorschau(wert, laenge) local roh = tostring(wert or ''):gsub('%s+', ' ') if #roh <= laenge then return roh end return roh:sub(1, laenge) .. '…' end --- Erreicht diese Nachricht den genannten Mitarbeiter? --- @param eintrag table Nachrichtenzeile --- @param mitarbeiter table Mitarbeiter --- @return boolean local function IstEmpfaenger(eintrag, mitarbeiter) if eintrag.empf_typ == 'rundmeldung' then return true end if eintrag.empf_typ == 'station' then return eintrag.empfaenger == mitarbeiter.station end return tonumber(eintrag.empfaenger) == mitarbeiter.charakterId end -- ===================================================================== -- ÜBERSICHT -- ===================================================================== Kern.RegistriereAktion('kommunikation', 'uebersicht', nil, function(quelle, mitarbeiter, daten) local ordner = tostring(daten.ordner or 'posteingang') local ich = mitarbeiter.charakterId local liste = {} if ordner == 'gesendet' then liste = MySQL.query.await([[ SELECT n.id, n.empfaenger, n.empf_typ, n.betreff, n.text, n.gesendet, (SELECT COUNT(*) FROM medical_nachricht_gelesen g WHERE g.nachricht = n.id) AS gelesenVon FROM medical_nachricht n WHERE n.absender = ? ORDER BY n.id DESC LIMIT 100 ]], { ich }) or {} for _, eintrag in ipairs(liste) do eintrag.empfaengerName = EmpfaengerName(eintrag) eintrag.vorschau = Vorschau(eintrag.text, 140) eintrag.gelesenVon = tonumber(eintrag.gelesenVon) or 0 eintrag.text = nil end elseif ordner == 'aushang' or ordner == 'archiv' then local aktiv = ordner == 'aushang' and 1 or 0 liste = MySQL.query.await([[ SELECT a.id, a.titel, a.text, a.kategorie, a.station, a.angeheftet, a.kenntnispflicht, a.frist, a.erstellt_von, a.erstellt_am, a.archiviert_von, a.archiviert_am, TIMESTAMPDIFF(MINUTE, NOW(), a.frist) AS restMinuten, (SELECT COUNT(*) FROM medical_aushang_kenntnis k WHERE k.aushang = a.id) AS kenntnisse, (SELECT 1 FROM medical_aushang_kenntnis k WHERE k.aushang = a.id AND k.`character` = ?) AS bestaetigt FROM medical_aushang a WHERE a.aktiv = ? AND (a.station IS NULL OR a.station = ?) ORDER BY a.angeheftet DESC, a.erstellt_am DESC LIMIT 100 ]], { ich, aktiv, mitarbeiter.station }) or {} for _, eintrag in ipairs(liste) do eintrag.autor = NameVon(eintrag.erstellt_von) eintrag.stationName = StationName(eintrag.station) eintrag.angeheftet = tonumber(eintrag.angeheftet) == 1 eintrag.kenntnispflicht = tonumber(eintrag.kenntnispflicht) == 1 eintrag.kenntnisse = tonumber(eintrag.kenntnisse) or 0 eintrag.bestaetigt = eintrag.bestaetigt ~= nil eintrag.restMinuten = eintrag.frist and (tonumber(eintrag.restMinuten) or 0) or nil -- Eine abgelaufene Frist ist ein Zustand, keine Folge eintrag.fristAbgelaufen = eintrag.restMinuten ~= nil and eintrag.restMinuten <= 0 eintrag.vorschau = Vorschau(eintrag.text, 200) eintrag.archiviertVon = eintrag.archiviert_von and NameVon(eintrag.archiviert_von) or nil eintrag.erstellt_von, eintrag.archiviert_von = nil, nil eintrag.text = nil end else liste = MySQL.query.await([[ SELECT n.id, n.absender, n.empfaenger, n.empf_typ, n.betreff, n.text, n.gesendet, c.firstname, c.lastname, (SELECT 1 FROM medical_nachricht_gelesen g WHERE g.nachricht = n.id AND g.`character` = ?) AS gelesen FROM medical_nachricht n JOIN aether_characters c ON c.id = n.absender WHERE n.empf_typ = 'rundmeldung' OR (n.empf_typ = 'person' AND n.empfaenger = ?) OR (n.empf_typ = 'station' AND n.empfaenger = ?) ORDER BY n.id DESC LIMIT 100 ]], { ich, tostring(ich), mitarbeiter.station or '' }) or {} for _, eintrag in ipairs(liste) do eintrag.absenderName = eintrag.firstname .. ' ' .. eintrag.lastname eintrag.empfaengerName = EmpfaengerName(eintrag) eintrag.vorschau = Vorschau(eintrag.text, 140) eintrag.gelesen = eintrag.gelesen ~= nil eintrag.anMich = eintrag.empf_typ == 'person' eintrag.firstname, eintrag.lastname, eintrag.text = nil, nil, nil end end -- Zählwerte für die Reiter local ungelesen = tonumber(MySQL.scalar.await([[ SELECT COUNT(*) FROM medical_nachricht n WHERE (n.empf_typ = 'rundmeldung' OR (n.empf_typ = 'person' AND n.empfaenger = ?) OR (n.empf_typ = 'station' AND n.empfaenger = ?)) AND NOT EXISTS (SELECT 1 FROM medical_nachricht_gelesen g WHERE g.nachricht = n.id AND g.`character` = ?) ]], { tostring(ich), mitarbeiter.station or '', ich })) or 0 -- Offene Kenntnisnahmen: was mich betrifft und was ich noch nicht bestätigt habe local offeneKenntnis = tonumber(MySQL.scalar.await([[ SELECT COUNT(*) FROM medical_aushang a WHERE a.aktiv = 1 AND a.kenntnispflicht = 1 AND (a.station IS NULL OR a.station = ?) AND NOT EXISTS (SELECT 1 FROM medical_aushang_kenntnis k WHERE k.aushang = a.id AND k.`character` = ?) ]], { mitarbeiter.station, ich })) or 0 local zaehler = { ungelesen = ungelesen, gesendet = tonumber(MySQL.scalar.await( 'SELECT COUNT(*) FROM medical_nachricht WHERE absender = ?', { ich })) or 0, aushaenge = tonumber(MySQL.scalar.await([[ SELECT COUNT(*) FROM medical_aushang WHERE aktiv = 1 AND (station IS NULL OR station = ?) ]], { mitarbeiter.station })) or 0, offeneKenntnis = offeneKenntnis, } return { ordner = ordner, liste = liste, zaehler = zaehler, kategorien = Medizin.Config.Aushangkategorien, stationen = Medizin.Config.Stationen, meineStation = mitarbeiter.station, rechte = { senden = Rechte.Hat(mitarbeiter, 'nachricht.senden'), rundmeldung = Rechte.Hat(mitarbeiter, 'nachricht.rundmeldung'), schreiben = Rechte.Hat(mitarbeiter, 'aushang.schreiben'), archivieren = Rechte.Hat(mitarbeiter, 'aushang.archivieren'), }, } end) -- ===================================================================== -- NACHRICHT LESEN -- -- Als gelesen gilt sie, weil jemand sie geöffnet hat — nicht, weil -- Zeit vergangen ist. -- ===================================================================== Kern.RegistriereAktion('kommunikation', 'nachricht', nil, function(quelle, mitarbeiter, daten) local nachrichtId = tonumber(daten.id) if not nachrichtId then return nil, 'Ungültige Kennung.' end local zeilen = MySQL.query.await([[ SELECT n.*, c.firstname, c.lastname FROM medical_nachricht n JOIN aether_characters c ON c.id = n.absender WHERE n.id = ? ]], { nachrichtId }) if not zeilen or not zeilen[1] then return nil, 'Nachricht nicht gefunden.' end local eintrag = zeilen[1] -- Fremde Post bleibt fremd local darfLesen = eintrag.absender == mitarbeiter.charakterId or IstEmpfaenger(eintrag, mitarbeiter) if not darfLesen then return nil, 'Diese Nachricht ist nicht an dich gerichtet.' end -- Nur Empfänger quittieren, nicht der Absender selbst if IstEmpfaenger(eintrag, mitarbeiter) then MySQL.insert.await([[ INSERT IGNORE INTO medical_nachricht_gelesen (nachricht, `character`) VALUES (?, ?) ]], { nachrichtId, mitarbeiter.charakterId }) end -- Wer hat sie gelesen? Für den Absender die wichtigste Angabe. local leser = {} if eintrag.absender == mitarbeiter.charakterId then leser = MySQL.query.await([[ SELECT g.gelesen_am, c.firstname, c.lastname FROM medical_nachricht_gelesen g JOIN aether_characters c ON c.id = g.`character` WHERE g.nachricht = ? ORDER BY g.gelesen_am ]], { nachrichtId }) or {} for _, zeile in ipairs(leser) do zeile.name = zeile.firstname .. ' ' .. zeile.lastname zeile.firstname, zeile.lastname = nil, nil end end return { nachricht = { id = eintrag.id, absender = eintrag.absender, absenderName = eintrag.firstname .. ' ' .. eintrag.lastname, empfaengerName = EmpfaengerName(eintrag), empfTyp = eintrag.empf_typ, betreff = eintrag.betreff, text = eintrag.text, gesendet = eintrag.gesendet, vonMir = eintrag.absender == mitarbeiter.charakterId, }, leser = leser, rechte = { senden = Rechte.Hat(mitarbeiter, 'nachricht.senden'), }, } end) Kern.RegistriereAktion('kommunikation', 'gelesen', nil, function(quelle, mitarbeiter, daten) local nachrichtId = tonumber(daten.id) if not nachrichtId then return nil, 'Ungültige Kennung.' end local zeilen = MySQL.query.await( 'SELECT id, empfaenger, empf_typ FROM medical_nachricht WHERE id = ?', { nachrichtId }) if not zeilen or not zeilen[1] then return nil, 'Nachricht nicht gefunden.' end if not IstEmpfaenger(zeilen[1], mitarbeiter) then return nil, 'Diese Nachricht ist nicht an dich gerichtet.' end MySQL.insert.await([[ INSERT IGNORE INTO medical_nachricht_gelesen (nachricht, `character`) VALUES (?, ?) ]], { nachrichtId, mitarbeiter.charakterId }) return { ok = true } end) -- ===================================================================== -- NACHRICHT SCHREIBEN -- -- Abgeschickt ist abgeschickt: es gibt hier bewusst kein Bearbeiten -- und kein Löschen. -- ===================================================================== --- Mögliche Empfänger: das Personal und die Stationen Kern.RegistriereAktion('kommunikation', 'empfaenger', 'nachricht.senden', function(quelle, mitarbeiter, daten) local personal = MySQL.query.await([[ SELECT p.`character` AS charakterId, p.personalnummer, p.rolle, p.station, c.firstname, c.lastname FROM medical_personal p JOIN aether_characters c ON c.id = p.`character` WHERE p.aktiv = 1 AND c.deleted = 0 ORDER BY p.personalnummer LIMIT 200 ]]) or {} -- Wer gerade im Dienst ist, wird gekennzeichnet — erreichbar sind alle local imDienst = {} for _, eintrag in pairs(Dienst.AlleImDienst()) do imDienst[eintrag.charakterId] = true end for _, eintrag in ipairs(personal) do eintrag.name = eintrag.firstname .. ' ' .. eintrag.lastname eintrag.rolleName = Medizin.HoleRolle(eintrag.rolle) and Medizin.HoleRolle(eintrag.rolle).name or eintrag.rolle eintrag.stationName = StationName(eintrag.station) eintrag.imDienst = imDienst[eintrag.charakterId] == true eintrag.selbst = eintrag.charakterId == mitarbeiter.charakterId eintrag.firstname, eintrag.lastname = nil, nil end return { personal = personal, stationen = Medizin.Config.Stationen, rechte = { rundmeldung = Rechte.Hat(mitarbeiter, 'nachricht.rundmeldung'), }, } end) Kern.RegistriereAktion('kommunikation', 'senden', 'nachricht.senden', function(quelle, mitarbeiter, daten) local typ = tostring(daten.typ or 'person') if typ ~= 'person' and typ ~= 'station' and typ ~= 'rundmeldung' then return nil, 'Unbekannte Empfängerart.' end -- Eine Rundmeldung erreicht das ganze Haus und braucht ein eigenes Recht if typ == 'rundmeldung' and not Rechte.Hat(mitarbeiter, 'nachricht.rundmeldung') then return nil, 'Rundmeldungen an alle darfst du nicht senden.' end local text = Text(daten.text, 4000) if not text or #text < 2 then return nil, 'Bitte einen Text schreiben.' end local betreff = Text(daten.betreff, 160) -- Empfänger prüfen und in die gespeicherte Schreibweise bringen local ziel, empfaengerName if typ == 'rundmeldung' then ziel = 'alle' empfaengerName = 'Alle im Haus' elseif typ == 'station' then local station = AusListe(Medizin.Config.Stationen, daten.ziel) if not station then return nil, 'Unbekannte Station.' end ziel = station empfaengerName = 'Station ' .. StationName(station) else local zielId = tonumber(daten.ziel) if not zielId then return nil, 'Kein Empfänger gewählt.' end local person = MySQL.scalar.await( 'SELECT `character` FROM medical_personal WHERE `character` = ? AND aktiv = 1', { zielId }) if not person then return nil, 'Diese Person gehört nicht zum Personal.' end ziel = tostring(zielId) empfaengerName = NameVon(zielId) end local nachrichtId = MySQL.insert.await([[ INSERT INTO medical_nachricht (absender, empfaenger, empf_typ, betreff, text) VALUES (?, ?, ?, ?, ?) ]], { mitarbeiter.charakterId, ziel, typ, betreff, text }) Protokoll.Schreibe(mitarbeiter, 'kommunikation', 'Nachricht gesendet', { typ = 'nachricht', id = nachrichtId }, { empfaengerTyp = typ, empfaenger = ziel, betreff = betreff }) -- Die Betroffenen erfahren es sofort — gelesen wird trotzdem von Hand local kopfzeile = betreff or Vorschau(text, 60) if typ == 'person' then Hinweise.Erstelle({ charakterId = tonumber(ziel) }, { kategorie = 'system', prioritaet = 'normal', titel = ('Nachricht von %s'):format(mitarbeiter.name), text = kopfzeile, zielTyp = 'nachricht', zielId = nachrichtId, }) else -- Station und Rundmeldung: nur ansprechen, wen sie erreicht for _, eintrag in pairs(Dienst.AlleImDienst()) do local passt = typ == 'rundmeldung' or eintrag.station == ziel if passt and eintrag.charakterId ~= mitarbeiter.charakterId then Hinweise.Erstelle({ charakterId = eintrag.charakterId }, { kategorie = 'system', prioritaet = typ == 'rundmeldung' and 'hoch' or 'normal', titel = typ == 'rundmeldung' and ('Rundmeldung von %s'):format(mitarbeiter.name) or ('Stationsnachricht von %s'):format(mitarbeiter.name), text = kopfzeile, zielTyp = 'nachricht', zielId = nachrichtId, }) end end end Kern.Loese('nachricht.gesendet', nachrichtId, typ, mitarbeiter) return { id = nachrichtId, empfaenger = empfaengerName } end) -- ===================================================================== -- SCHWARZES BRETT -- ===================================================================== Kern.RegistriereAktion('kommunikation', 'aushang', nil, function(quelle, mitarbeiter, daten) local aushangId = tonumber(daten.id) if not aushangId then return nil, 'Ungültige Kennung.' end local zeilen = MySQL.query.await([[ SELECT a.*, TIMESTAMPDIFF(MINUTE, NOW(), a.frist) AS restMinuten, (SELECT 1 FROM medical_aushang_kenntnis k WHERE k.aushang = a.id AND k.`character` = ?) AS bestaetigt FROM medical_aushang a WHERE a.id = ? ]], { mitarbeiter.charakterId, aushangId }) if not zeilen or not zeilen[1] then return nil, 'Aushang nicht gefunden.' end local eintrag = zeilen[1] -- Wer hat gelesen und wer noch nicht? local kenntnis = MySQL.query.await([[ SELECT k.bestaetigt_am, c.firstname, c.lastname, p.personalnummer FROM medical_aushang_kenntnis k JOIN aether_characters c ON c.id = k.`character` LEFT JOIN medical_personal p ON p.`character` = k.`character` WHERE k.aushang = ? ORDER BY k.bestaetigt_am ]], { aushangId }) or {} for _, zeile in ipairs(kenntnis) do zeile.name = zeile.firstname .. ' ' .. zeile.lastname zeile.firstname, zeile.lastname = nil, nil end -- Wen der Aushang betrifft, der aber noch nicht bestätigt hat. -- Reine Auskunft — niemand wird dadurch zu etwas gezwungen. local offen = {} if tonumber(eintrag.kenntnispflicht) == 1 then offen = MySQL.query.await([[ SELECT p.personalnummer, p.station, c.firstname, c.lastname FROM medical_personal p JOIN aether_characters c ON c.id = p.`character` WHERE p.aktiv = 1 AND (? IS NULL OR p.station = ?) AND NOT EXISTS (SELECT 1 FROM medical_aushang_kenntnis k WHERE k.aushang = ? AND k.`character` = p.`character`) ORDER BY p.personalnummer LIMIT 100 ]], { eintrag.station, eintrag.station, aushangId }) or {} for _, zeile in ipairs(offen) do zeile.name = zeile.firstname .. ' ' .. zeile.lastname zeile.firstname, zeile.lastname = nil, nil end end local restMinuten = eintrag.frist and (tonumber(eintrag.restMinuten) or 0) or nil return { aushang = { id = eintrag.id, titel = eintrag.titel, text = eintrag.text, kategorie = eintrag.kategorie, station = eintrag.station, stationName = StationName(eintrag.station), angeheftet = tonumber(eintrag.angeheftet) == 1, kenntnispflicht = tonumber(eintrag.kenntnispflicht) == 1, frist = eintrag.frist, restMinuten = restMinuten, fristAbgelaufen = restMinuten ~= nil and restMinuten <= 0, autor = NameVon(eintrag.erstellt_von), erstelltAm = eintrag.erstellt_am, aktiv = tonumber(eintrag.aktiv) == 1, archiviertAm = eintrag.archiviert_am, archiviertVon = eintrag.archiviert_von and NameVon(eintrag.archiviert_von) or nil, bestaetigt = eintrag.bestaetigt ~= nil, -- Ein Aushang, den schon jemand quittiert hat, wird im Wortlaut -- nicht mehr angefasst — sonst hätte jemand etwas anderes gelesen textGesperrt = #kenntnis > 0, }, kenntnis = kenntnis, offen = offen, kategorien = Medizin.Config.Aushangkategorien, stationen = Medizin.Config.Stationen, rechte = { schreiben = Rechte.Hat(mitarbeiter, 'aushang.schreiben'), archivieren = Rechte.Hat(mitarbeiter, 'aushang.archivieren'), }, } end) Kern.RegistriereAktion('kommunikation', 'aushangSchreiben', 'aushang.schreiben', function(quelle, mitarbeiter, daten) local aushangId = tonumber(daten.id) local titel = Text(daten.titel, 160) local text = Text(daten.text, 8000) local kategorie = AusListe(Medizin.Config.Aushangkategorien, daten.kategorie) if not titel then return nil, 'Bitte einen Titel angeben.' end if not text or #text < 5 then return nil, 'Bitte einen Text schreiben.' end if not kategorie then return nil, 'Unbekannte Kategorie.' end -- Ohne Station gilt der Aushang für das ganze Haus local station = nil if Text(daten.station, 32) then station = AusListe(Medizin.Config.Stationen, daten.station) if not station then return nil, 'Unbekannte Station.' end end local angeheftet = daten.angeheftet == true and 1 or 0 local kenntnispflicht = daten.kenntnispflicht == true and 1 or 0 -- Eine Frist erinnert nur. Sie sperrt nichts und verfällt folgenlos. local frist = Text(daten.frist, 20) if frist and not frist:match('^%d%d%d%d%-%d%d%-%d%d[ T]%d%d:%d%d') then return nil, 'Die Frist braucht die Form JJJJ-MM-TT SS:MM.' end if frist then frist = frist:gsub('T', ' '):sub(1, 16) .. ':00' end if aushangId then local vorher = MySQL.query.await( 'SELECT id, titel, text, aktiv FROM medical_aushang WHERE id = ?', { aushangId }) if not vorher or not vorher[1] then return nil, 'Aushang nicht gefunden.' end if tonumber(vorher[1].aktiv) ~= 1 then return nil, 'Ein archivierter Aushang wird nicht mehr bearbeitet.' end -- Wer schon quittiert hat, hat einen bestimmten Wortlaut gelesen local quittiert = tonumber(MySQL.scalar.await( 'SELECT COUNT(*) FROM medical_aushang_kenntnis WHERE aushang = ?', { aushangId })) or 0 if quittiert > 0 and (vorher[1].titel ~= titel or vorher[1].text ~= text) then return nil, ('%d Person(en) haben diesen Wortlaut bereits zur Kenntnis genommen. Bitte archivieren und neu veröffentlichen.') :format(quittiert) end MySQL.update.await([[ UPDATE medical_aushang SET titel = ?, text = ?, kategorie = ?, station = ?, angeheftet = ?, kenntnispflicht = ?, frist = ? WHERE id = ? ]], { titel, text, kategorie, station, angeheftet, kenntnispflicht, frist, aushangId }) Protokoll.Schreibe(mitarbeiter, 'kommunikation', 'Aushang bearbeitet', { typ = 'aushang', id = aushangId }, { titel = titel, kategorie = kategorie, station = station, kenntnispflicht = kenntnispflicht == 1, frist = frist }) else aushangId = MySQL.insert.await([[ INSERT INTO medical_aushang (titel, text, kategorie, station, angeheftet, kenntnispflicht, frist, erstellt_von) VALUES (?, ?, ?, ?, ?, ?, ?, ?) ]], { titel, text, kategorie, station, angeheftet, kenntnispflicht, frist, mitarbeiter.charakterId }) Protokoll.Schreibe(mitarbeiter, 'kommunikation', 'Aushang veröffentlicht', { typ = 'aushang', id = aushangId }, { titel = titel, kategorie = kategorie, station = station, kenntnispflicht = kenntnispflicht == 1, frist = frist }) -- Nur die ansprechen, die der Aushang betrifft for _, eintrag in pairs(Dienst.AlleImDienst()) do local passt = station == nil or eintrag.station == station if passt then Hinweise.Erstelle({ charakterId = eintrag.charakterId }, { kategorie = 'system', prioritaet = kenntnispflicht == 1 and 'hoch' or 'normal', titel = ('Neuer Aushang · %s') :format(kategorie == 'anweisung' and 'Dienstanweisung' or 'Schwarzes Brett'), text = titel, zielTyp = 'aushang', zielId = aushangId, }) end end end Kern.Loese('aushang.geaendert', aushangId, mitarbeiter) return { id = aushangId } end) Kern.RegistriereAktion('kommunikation', 'anheften', 'aushang.schreiben', function(quelle, mitarbeiter, daten) local aushangId = tonumber(daten.id) if not aushangId then return nil, 'Ungültige Kennung.' end local zeilen = MySQL.query.await( 'SELECT id, titel, angeheftet FROM medical_aushang WHERE id = ? AND aktiv = 1', { aushangId }) if not zeilen or not zeilen[1] then return nil, 'Aushang nicht gefunden.' end local neu = daten.angeheftet == true and 1 or 0 MySQL.update.await('UPDATE medical_aushang SET angeheftet = ? WHERE id = ?', { neu, aushangId }) Protokoll.Schreibe(mitarbeiter, 'kommunikation', neu == 1 and 'Aushang angeheftet' or 'Aushang losgelöst', { typ = 'aushang', id = aushangId }, { titel = zeilen[1].titel }) return { ok = true } end) Kern.RegistriereAktion('kommunikation', 'aushangArchivieren', 'aushang.archivieren', function(quelle, mitarbeiter, daten) local aushangId = tonumber(daten.id) if not aushangId then return nil, 'Ungültige Kennung.' end local grund = Text(daten.grund, 255) if not grund then return nil, 'Bitte angeben, warum der Aushang abgenommen wird.' end local zeilen = MySQL.query.await( 'SELECT id, titel, aktiv, kenntnispflicht FROM medical_aushang WHERE id = ?', { aushangId }) if not zeilen or not zeilen[1] then return nil, 'Aushang nicht gefunden.' end if tonumber(zeilen[1].aktiv) ~= 1 then return nil, 'Dieser Aushang hängt nicht mehr.' end -- Offene Kenntnisnahmen halten nicht auf, werden aber festgehalten local offen = 0 if tonumber(zeilen[1].kenntnispflicht) == 1 then offen = tonumber(MySQL.scalar.await([[ SELECT COUNT(*) FROM medical_personal p WHERE p.aktiv = 1 AND NOT EXISTS (SELECT 1 FROM medical_aushang_kenntnis k WHERE k.aushang = ? AND k.`character` = p.`character`) ]], { aushangId })) or 0 end MySQL.update.await([[ UPDATE medical_aushang SET aktiv = 0, angeheftet = 0, archiviert_von = ?, archiviert_am = NOW() WHERE id = ? ]], { mitarbeiter.charakterId, aushangId }) Protokoll.Schreibe(mitarbeiter, 'kommunikation', 'Aushang archiviert', { typ = 'aushang', id = aushangId }, { titel = zeilen[1].titel, offeneKenntnisnahmen = offen }, grund) Kern.Loese('aushang.archiviert', aushangId, mitarbeiter) return { ok = true, offen = offen } end) -- ===================================================================== -- KENNTNISNAHME -- -- Bestätigt ausschliesslich, dass jemand selbst gelesen hat. Sie lässt -- sich nicht für andere setzen, nicht zurücknehmen und ändert nichts -- ausser sich selbst. -- ===================================================================== Kern.RegistriereAktion('kommunikation', 'kenntnis', nil, function(quelle, mitarbeiter, daten) local aushangId = tonumber(daten.id) if not aushangId then return nil, 'Ungültige Kennung.' end local zeilen = MySQL.query.await( 'SELECT id, titel, station, aktiv FROM medical_aushang WHERE id = ?', { aushangId }) if not zeilen or not zeilen[1] then return nil, 'Aushang nicht gefunden.' end local aushang = zeilen[1] if tonumber(aushang.aktiv) ~= 1 then return nil, 'Dieser Aushang hängt nicht mehr.' end if aushang.station and aushang.station ~= mitarbeiter.station then return nil, 'Dieser Aushang betrifft eine andere Station.' end local schon = MySQL.scalar.await([[ SELECT 1 FROM medical_aushang_kenntnis WHERE aushang = ? AND `character` = ? ]], { aushangId, mitarbeiter.charakterId }) if schon then return { ok = true, schonBestaetigt = true } end MySQL.insert.await([[ INSERT INTO medical_aushang_kenntnis (aushang, `character`) VALUES (?, ?) ]], { aushangId, mitarbeiter.charakterId }) Protokoll.Schreibe(mitarbeiter, 'kommunikation', 'Kenntnis genommen', { typ = 'aushang', id = aushangId }, { titel = aushang.titel }) return { ok = true } end) -- --------------------------------------------------------------------- -- Schnittstelle für andere Ressourcen -- --------------------------------------------------------------------- --- Liefert die aktiven Aushänge einer Station. --- Reine Auskunft — keine Ressource kann darüber etwas veröffentlichen --- oder eine Kenntnisnahme setzen. --- @param station string|nil Stationskennung --- @return table Liste exports('HoleAushaenge', function(station) return MySQL.query.await([[ SELECT a.id, a.titel, a.text, a.kategorie, a.station, a.angeheftet, a.kenntnispflicht, a.frist, a.erstellt_am FROM medical_aushang a WHERE a.aktiv = 1 AND (a.station IS NULL OR a.station = ?) ORDER BY a.angeheftet DESC, a.erstellt_am DESC ]], { station }) or {} end)