/** * Die eingebauten Platzhalter (#010, aufgeloest in #012). * * Ein Platzhalter `{{schluessel}}` in Titel, Hilfetext oder Befehl wird beim * Abarbeiten eines Laufs ersetzt. Woraus, steht hier: entweder aus einer * Variablen der Vorlage oder aus einer der eingebauten Quellen. Ein Schluessel, * der zu keiner von beiden passt, bleibt im Lauf als `{{schluessel}}` stehen — * sichtbar, aber nutzlos. Deshalb warnt der Editor davor. * * Die Liste ist bewusst Daten und kein Code: der Editor zeigt sie als Hilfe an, * die Warnung vergleicht dagegen, und #012 baut die Aufloesung darauf auf. Eine * zweite Aufzaehlung an anderer Stelle wuerde unweigerlich auseinanderlaufen. */ export type PlatzhalterQuelle = "GERAET_LAUF" | "MITARBEITER" | "ABTEILUNG" | "EINSTELLUNG" | "BESONDERS"; export type EingebauterPlatzhalter = { readonly schluessel: string; readonly quelle: PlatzhalterQuelle; readonly erklaerung: string; /** Nur im Modus DIENSTLEISTUNG gefuellt; im Modus INTERN bleibt der Wert leer. */ readonly nurDienstleistung?: true; }; export const EINGEBAUTE_PLATZHALTER: readonly EingebauterPlatzhalter[] = [ // Aus Geraet und Lauf { schluessel: "hostname", quelle: "GERAET_LAUF", erklaerung: "Hostname des Geräts im Lauf" }, { schluessel: "seriennummer", quelle: "GERAET_LAUF", erklaerung: "Seriennummer des Geräts" }, { schluessel: "inventarnummer", quelle: "GERAET_LAUF", erklaerung: "Inventarnummer des Geräts" }, { schluessel: "ticket", quelle: "GERAET_LAUF", erklaerung: "Ticketnummer am Lauf" }, { schluessel: "datum", quelle: "GERAET_LAUF", erklaerung: "heutiges Datum" }, { schluessel: "techniker", quelle: "GERAET_LAUF", erklaerung: "Name des Bearbeiters" }, { schluessel: "kunde", quelle: "GERAET_LAUF", erklaerung: "Name des Kunden", nurDienstleistung: true, }, { schluessel: "kundenkuerzel", quelle: "GERAET_LAUF", erklaerung: "Kürzel des Kunden", nurDienstleistung: true, }, // Aus dem Mitarbeiter { schluessel: "mitarbeiter", quelle: "MITARBEITER", erklaerung: "vollständiger Name" }, { schluessel: "benutzer", quelle: "MITARBEITER", erklaerung: "gleichbedeutend mit mitarbeiter" }, { schluessel: "vorname", quelle: "MITARBEITER", erklaerung: "Vorname" }, { schluessel: "nachname", quelle: "MITARBEITER", erklaerung: "Nachname" }, { schluessel: "upn", quelle: "MITARBEITER", erklaerung: "User Principal Name" }, { schluessel: "sam", quelle: "MITARBEITER", erklaerung: "sAMAccountName, höchstens 20 Zeichen" }, { schluessel: "mail", quelle: "MITARBEITER", erklaerung: "Mail-Adresse" }, { schluessel: "abteilung", quelle: "MITARBEITER", erklaerung: "Name der Abteilung" }, { schluessel: "titel", quelle: "MITARBEITER", erklaerung: "Funktion oder Titel" }, { schluessel: "vorgesetzter", quelle: "MITARBEITER", erklaerung: "Name des Vorgesetzten" }, { schluessel: "vorgesetzter_sam", quelle: "MITARBEITER", erklaerung: "sAMAccountName des Vorgesetzten" }, { schluessel: "dn", quelle: "MITARBEITER", erklaerung: "Distinguished Name im Verzeichnis" }, // Aus der Abteilung { schluessel: "ou", quelle: "ABTEILUNG", erklaerung: "Ziel-Container: Eltern-Container des dn, sonst die Ziel-OU der Abteilung", }, { schluessel: "gruppen", quelle: "ABTEILUNG", erklaerung: "Standardgruppen, kommagetrennt" }, // Aus den Einstellungen { schluessel: "ou_gesperrt", quelle: "EINSTELLUNG", erklaerung: "Ziel-OU für deaktivierte Konten, aus Admin → Einstellungen", }, // Besonders { schluessel: "generiertes_passwort", quelle: "BESONDERS", erklaerung: "20 Zeichen, bei jeder Anzeige neu erzeugt und nirgends gespeichert — auch nicht im Protokoll", }, ]; export const QUELLEN_TEXT: Record = { GERAET_LAUF: "Gerät und Lauf", MITARBEITER: "Mitarbeiter", ABTEILUNG: "Abteilung", EINSTELLUNG: "Einstellungen", BESONDERS: "Besonders", }; const EINGEBAUTE_SCHLUESSEL: ReadonlySet = new Set( EINGEBAUTE_PLATZHALTER.map((platzhalter) => platzhalter.schluessel), ); export function istEingebaut(schluessel: string): boolean { return EINGEBAUTE_SCHLUESSEL.has(schluessel); } /** * Eingebaute Platzhalter, die eine Vorlagenvariable **nicht** belegen darf. * * Es sind die fuenf, die eine Aussage ueber **wer, wann und fuer wen** machen: * * - `techniker` und `datum` unterschreiben einen Befehl. Eine Variable dafuer * hiesse, dass in einem fertigen Befehl ein fremder Name und ein falsches * Datum stehen koennen, ohne dass es auffaellt. * - `kunde` und `kundenkuerzel` sind im Modus INTERN ausdruecklich leer (#008). * Eine Variable haette die Kundenebene in den Befehl zurueckgeholt, die es * dort nicht gibt. * - `generiertes_passwort` wird ohnehin vor jeder Zuordnung abgefangen; hier * steht es, damit niemand eine Variable anlegt, die nie wirkt. * * Alles andere bleibt erlaubt, und das ist wichtig: beim Onboarding gibt es die * Person im Verzeichnis noch nicht, die Vorlage fragt `upn`, `sam` und `mail` * also selbst ab. Genau dafuer sind Variablen da. */ export const GESPERRTE_VARIABLENNAMEN: ReadonlySet = new Set([ "techniker", "datum", "kunde", "kundenkuerzel", "generiertes_passwort", ]); /** Darf eine Vorlagenvariable so heissen? */ export function istAlsVariableErlaubt(schluessel: string): boolean { return !GESPERRTE_VARIABLENNAMEN.has(schluessel); } /** * Die Platzhalter, die weder eingebaut noch als Variable definiert sind. * * Das ist eine **Warnung**, keine Ablehnung: ein Tippfehler faellt so im Editor * auf, statt erst dem Techniker im Lauf. Speichern und Veroeffentlichen bleiben * erlaubt — es kann gute Gruende geben, einen Platzhalter stehen zu lassen, etwa * wenn die Variable noch kommt. */ export function unbekanntePlatzhalter( verwendete: readonly string[], variablenSchluessel: readonly string[], ): readonly string[] { const bekannt = new Set(variablenSchluessel); return verwendete.filter((schluessel) => !bekannt.has(schluessel) && !istEingebaut(schluessel)); } /** Variablen, die definiert sind, aber nirgends verwendet werden. */ export function unbenutzteVariablen( verwendete: readonly string[], variablenSchluessel: readonly string[], ): readonly string[] { const benutzt = new Set(verwendete); return variablenSchluessel.filter((schluessel) => !benutzt.has(schluessel)); }