| Eigenschaft | Wert |
|---|---|
| Plugin-ID | com.osticket:markdown-support |
| Name | Markdown Support |
| Plugin-Klasse | MarkdownPlugin in class.MarkdownPlugin.php |
| Konfigurationsklasse | MarkdownConfig in config.php |
| Instanzen | Singleton: genau eine Instanz, enable() legt sie bei Bedarf an |
| PHP | 8.0 oder neuer: composer.json deklariert >=7.4, aber ConfigCache verwendet den PHP-8.0-Typ mixed. Unter PHP 7.4 lösen get() und set() einen TypeError aus |
| Abhängigkeit | erusev/parsedown ^1.7 (gesperrt auf 1.7.4) |
Das Plugin braucht zwei Eingriffe in osTicket:
ThreadEntryBody::$types um den Wert markdown. Dafür ist keine Dateiänderung nötig.include/class.thread.php hinter dem Zweig case 'html': von ThreadEntryBody::fromFormattedText() den Zweig case 'markdown': return new MarkdownThreadEntryBody(...) ein. Kann es das Muster nicht finden (andere osTicket-Version), loggt es Unable to apply patch - pattern not found in das PHP-Error-Log und patcht nicht.Enthält die Datei bereits einen case 'markdown':, tut das Plugin nichts. Vor dem Patch kopiert es die Datei nach include/class.thread.php.markdown-backup, sofern noch keine Sicherung existiert. Beim Deaktivieren stellt es die Datei aus dieser Sicherung wieder her und löscht sie. Fehlt die Sicherung, stellt es nichts wieder her. Eine bereits vorhandene Sicherung wird nicht überschrieben (siehe Installation, Abschnitt osTicket aktualisieren).
MarkdownThreadEntryBody erweitert ThreadEntryBody mit dem Typ markdown und rendert in display():
Parsedown mit setSafeMode(true) wandelt Markdown zu HTML.Format::sanitize() von osTicket bereinigt das Ergebnis.pdf ersetzt das Plugin img-Tags mit http(s)-Quelle durch den Text [Image]. Im Modus email bleibt das HTML unverändert.Die Suchindizierung (getSearchable()) wandelt das gerenderte HTML mit Format::html2text() in Text um.
osTicket verwendet für E-Mails den rohen POST-Text. Der Markdown-Editor sendet Klartext, deshalb würden E-Mails sonst Markdown-Syntax zeigen. Das Plugin geht so vor:
format=markdown sichert es die Felder response, note und message, rendert sie mit MarkdownThreadEntryBody zu HTML und setzt format für osTicket auf html.threadentry.created merkt sich die ID des Eintrags.format = 'markdown' per SQL in THREAD_ENTRY_TABLE zurück.Zusätzlich korrigiert der Handler das Format über threadentry.created, wenn die Anfrage ein gültiges format mitbringt (siehe API-Integration).
object.view: Bei einem Ticket gibt das Plugin den Konfigurationsblock window.osTicketMarkdownConfig (defaultFormat, allowFormatSwitch, showToolbar, previewApiUrl), das Stylesheet und das Script js/markdown-editor.js aus. Die Dateien erhalten ?v=<installierte Version> als Cache-Parameter.tickets.php oder ticket.php (und ajax.php-URLs mit ticket) setzt das Plugin zusätzlich einen Output-Buffer, der dieselben Assets vor </head> einfügt. Doppeltes Einfügen verhindert die Prüfung auf window.osTicketMarkdownConfig.markdown-preview.php wird beim Aktivieren (und bei jedem Start, wenn die Datei abweicht) nach api/markdown-preview.php kopiert, weil include/ per .htaccess gesperrt ist. Der Endpoint:
POST mit JSON {"markdown": "..."} und antwortet mit {"success": true, "html": "..."},javascript:- und data:-Werte in href/src, on*-Attribute, style mit expression() und script-Tags,markdown nicht,vendor/autoload.php neben der Datei oder unter ../include/plugins/markdown-support/vendor/autoload.php.Der Endpoint existiert unter zwei Pfaden: als
api/markdown-preview.php(vom Plugin dorthin kopiert) und als Original im Plugin-Ordnerinclude/plugins/markdown-support/markdown-preview.php, das die.htaccessdes Plugins freigibt. Beide Pfade sind ohne Anmeldung erreichbar, prüfen kein CSRF-Token, begrenzen die Größe der Eingabe nicht und laden osTicket nicht. Die Live-Vorschau im Editor nutzt nur die Kopie inapi/.
Die Vorschau läuft nicht durch
Format::sanitize(), sondern nur durch den eigenen Filter. Sie zeigt deshalb nicht in jedem Fall exakt das HTML, das später in der Ticket-Ansicht erscheint.
| Maßnahme | Wo |
|---|---|
| Parsedown SafeMode (HTML im Text wird maskiert, unsichere URL-Schemata werden entschärft) | Rendering und Vorschau |
Format::sanitize() (osTicket) |
Rendering von Thread-Einträgen |
Regex-Filter für javascript:/data:-URLs, on*, expression(), script |
Preview-Endpoint |
Bild-Upload: MIME-Whitelist (JPEG, PNG, GIF, WebP, BMP), URL-Prüfung, Alt-Text ohne [](), Abbruch ohne CSRF-Token |
Editor im Browser |
Eigene .htaccess im Plugin-Ordner: gibt markdown-preview.php frei, sperrt *.log |
Plugin-Ordner |
SQL-Werte über db_input() |
Format- und Body-Updates |
Die Klasse MarkdownSanitizer ist mit Tests enthalten, wird im Plugin-Ablauf aber nicht aufgerufen.
include/.htaccessBeim Aktivieren fügt das Plugin zwei Blöcke ein: einen FilesMatch-Block, der js, css, map, json, Bilder und Fonts freigibt, und einen Block für PHP-Dateien unter plugins/. Beim Deaktivieren entfernt es beide Blöcke wieder. Für NGINX gibt es keine automatische Anpassung.
Die Regel für PHP-Dateien (<FilesMatch "^plugins/.+\.php$">) wird von Apache nur gegen Dateinamen ohne Pfad geprüft und greift deshalb vermutlich nie. Geöffnet wird markdown-preview.php durch die Files-Direktive in der .htaccess des Plugin-Ordners.
markdown-support/
├── plugin.php # Plugin-Metadaten
├── class.MarkdownPlugin.php # Plugin-Klasse, Signale, enable/disable
├── config.php # MarkdownConfig mit den Optionen
├── markdown-body.php # MarkdownThreadEntryBody
├── markdown-preview.php # Preview-Endpoint (Kopie landet in api/)
├── MarkdownDetector.php # Erkennung von Markdown-Syntax
├── .htaccess # Freigabe des Endpoints, Sperre für Logs
├── src/
│ ├── Config/ConfigCache.php
│ ├── Core/CorePatcher.php
│ ├── Asset/AssetInjector.php
│ ├── Asset/AssetDeployer.php
│ ├── Http/MarkdownPostProcessor.php
│ ├── Signal/ThreadEntryHandler.php
│ └── Format/ # Sanitizer und Interfaces
├── css/markdown-editor.css
├── js/markdown-editor.js # gebündelt aus js/src/
├── i18n/de_DE/ # Deutsche Übersetzung der Konfiguration
└── vendor/ # Parsedown (Composer)
Das Repository enthält zusätzlich Tests (PHPUnit), Build-Quellen für das JavaScript und Dokumentation. Die CI führt eine Syntaxprüfung (php -l) mit PHP 7.4 bis 8.3 aus.