Subticket Manager: Technische Details
Technische Details des osTicket-Plugins Subticket Manager: Startablauf, Auto-Verknüpfung, Dateistruktur, Datenbank-Änderungen, Deployment nach scp/, Schutzmechanismen und Einschränkungen.
Subticket Manager: Technische Details
Plugin-Metadaten
| Eigenschaft | Wert |
|---|---|
| Plugin-ID | subticket-manager |
| Name | Subticket Manager |
| Plugin-Klasse | SubticketPlugin in class.SubticketPlugin.php |
| Konfigurationsklasse | SubticketPluginConfig in config.php |
| Instanzen | Singleton, enable() legt die Instanz automatisch an |
| Lizenz | GNU GPL v2 (Datei LICENSE) |
Startablauf
Bei jedem Seitenaufruf, bei dem osTicket das aktive Plugin lädt, läuft bootstrap() in dieser Reihenfolge:
graph TD
A["bootstrap()"] --> B["Services anlegen"]
B --> C["Konfiguration in Zwischenspeicher"]
C --> D["subticket_parent aus URL in Session"]
D --> E["scp-Dateien neu kopieren, wenn Version abweicht"]
E --> F["Tabellen, Spalte und Indizes anlegen"]
F --> G["Staff-App Subtickets registrieren"]
G --> H["Signal object.view nur im Staff-Bereich"]
H --> I["Signale model.updated und model.created"]
I --> J["Queue-Indikator-Skript auf Queue-Seiten"]
Auto-Verknüpfung beim Anlegen
sequenceDiagram
participant A as Agent
participant P as Panel
participant O as osTicket
participant H as TicketEventHandler
A->>P: Create Subticket
P->>O: Formular Neues Ticket mit Parent-ID
O->>H: bootstrap speichert ID in der Session
A->>O: Ticket anlegen
O->>H: Signal model.created
H->>O: UPDATE ost_ticket SET ticket_pid
Der Handler nimmt die ID nur an, wenn sie numerisch und größer als 0 ist. Beim Anlegen setzt er ticket_pid per direktem UPDATE, ohne Existenz-, Schleifen- oder Abteilungsprüfung, und löscht die ID danach aus der Session.
Dateistruktur
subticket-manager/
├── plugin.php # Metadaten
├── class.SubticketPlugin.php # Plugin-Klasse, Lebenszyklus, Signal-Anbindung
├── config.php # Konfigurationsformular
├── queue-decoration.php # ParentTicketDecoration
├── ajax/
│ ├── SubticketController.php # Logik hinter ajax-subticket.php
│ └── PluginPathResolver.php # Hilfsfunktion für Tests
├── scp-files/ # werden nach scp/ kopiert
│ ├── subtickets.php # Übersichtsseite
│ ├── ajax-subticket.php # AJAX-Einstiegspunkt
│ └── apps.php # Anwendungs-Übersicht
├── js/
│ ├── subticket-panel.js # Panel-Interaktionen
│ └── queue-indicator.js # Icons in der Warteschlange
└── src/ # PSR-4, Namespace SubticketManager
├── Asset/AssetDeployer.php # Kopieren und Entfernen der scp-Dateien
├── Config/ConfigCache.php # Zwischenspeicher für Konfigurationswerte
├── Database/DatabaseService.php # Tabellen, Spalte, Indizes, Fremdschlüssel
├── Hierarchy/HierarchyService.php # Verknüpfen, Trennen, Schleifenprüfung
├── Signal/TicketEventHandler.php # Signal-Handler
└── UI/PanelRenderer.php # HTML, CSS und Skript-Loader des Panels
Datenbank-Änderungen
Die Beziehungen selbst stehen in der osTicket-Spalte ost_ticket.ticket_pid. Das Plugin ergänzt beim Start, falls noch nicht vorhanden:
| Objekt | Definition |
|---|---|
Tabelle ost_ticket_hierarchy_metadata |
ticket_id (Primärschlüssel), auto_close_enabled, inherit_settings, dependency_type, max_children, created, updated |
Tabelle ost_ticket_progress |
parent_id (Primärschlüssel), total_children, completed_children, in_progress_children, pending_children, last_calculated |
Spalte ost_ticket.version |
int(11) DEFAULT 0 |
Index idx_ticket_pid |
auf ost_ticket(ticket_pid) |
Index idx_ticket_hierarchy |
auf ost_ticket(ticket_id, ticket_pid, status_id) |
Fremdschlüssel fk_hierarchy_ticket, fk_progress_ticket |
auf ost_ticket(ticket_id) mit ON DELETE CASCADE, nur wenn für die Tabelle noch kein Fremdschlüssel besteht |
Die beiden Tabellen und die Spalte
versionwerden vom Plugin angelegt, aber nirgends gelesen oder beschrieben. Die Funktionen des Plugins kommen allein mitticket_pidaus.
Die Prüfungen laufen bei jedem Start des Plugins (also bei jedem osTicket-Seitenaufruf): als SHOW-Abfragen für Tabellen, Spalte und Indizes und je Tabelle als SELECT auf information_schema.TABLE_CONSTRAINTS für den Fremdschlüssel. Beim Deinstallieren entfernt das Plugin die Objekte nur, wenn Remove Data on Uninstall aktiv ist (siehe Konfiguration).
Deployment nach scp/
Der AssetDeployer kopiert scp-files/subtickets.php, scp-files/ajax-subticket.php und scp-files/apps.php nach scp/. Er läuft bei enable() und bei jedem Start, wenn die Datei scp/.subticket-deployed-version fehlt oder eine andere Version als plugin.php enthält. Die Zielverzeichnis- und Schreibrechte-Prüfung liefert die Fehlermeldungen aus Installation.
Regeln und Schutzmechanismen
| Bereich | Verhalten |
|---|---|
| Verknüpfen | ID größer 0, kein Selbstbezug, beide Tickets müssen existieren, keine Schleife |
| Schleifenprüfung | isDescendant() folgt der Kette ticket_pid höchstens zehn Ebenen. Bei tieferen Ketten endet die Prüfung ohne Treffer. Eine Begrenzung der Hierarchietiefe gibt es nicht |
| Mehrere Parents | Nicht möglich, ein Ticket hat genau eine Spalte ticket_pid |
| Eingaben | Ticket-IDs werden als positive Zahlen validiert, SQL-Werte laufen durch db_input() |
| CSRF | link, unlink und create verlangen ein gültiges osTicket-CSRF-Token |
| Abteilungszugriff | Prüfung über canAccessDept(), Administratoren sind ausgenommen |
| Rate-Limit | 100 Anfragen pro 60 Sekunden und Session für children, link, unlink, create |
| Panel-Ausgabe | Nummer, Betreff und Status werden mit htmlspecialchars ausgegeben |
Kompatibilitätsprüfung
Die Plugin-Klasse enthält isCompatible(). Die Methode meldet false, wenn die osTicket-Version unter 1.18.0 liegt, PHP unter 7.4 läuft oder die Spalte ticket_pid in ost_ticket fehlt.
Einschränkungen
- Der Tabellenpräfix
ost_ist in allen SQL-Abfragen fest eingetragen. - Die Ticketnummer beim Verknüpfen muss numerisch sein (AJAX-Schnittstelle und Dialog).
- Die Aktion
childrenund die Übersichtsseite prüfen keine Abteilungsrechte. - Beim Verknüpfen prüft das Plugin die Abteilung des Child-Tickets, nicht die des Parents.
- Die Konfigurationsfelder außer Remove Data on Uninstall haben keine Wirkung.