The Markdown Support Plugin adds full Markdown formatting capabilities to osTicket 1.18.x ticket threads with a user-friendly editor interface, live preview, and automatic format detection.
Without this plugin: Ticket responses can only be formatted as plain text or via the WYSIWYG HTML editor. Technical content like code snippets or structured documentation is cumbersome to create.
With this plugin: Markdown syntax is directly supported in the editor - with live preview, toolbar buttons, and keyboard shortcuts. Code blocks, lists, and links are formatted in seconds.
Technical support teams:
Use Markdown for code blocks and structured documentation - ideal for software support, DevOps, and IT teams.
API integrations:
Create tickets via API with Markdown content. Auto-detection recognizes Markdown syntax even without an explicit format parameter.
Internal notes:
Quick formatting for internal staff notes without WYSIWYG complexity - lists, code snippets, and links in seconds.
Copy-paste from GitHub/GitLab:
Markdown content from issue trackers can be seamlessly pasted into osTicket tickets and renders correctly.
Screenshots and images:
Paste screenshots directly from clipboard or drag & drop images into ticket responses - ideal for bug reports and technical documentation.
Canned responses in Markdown format:
Existing HTML canned responses are automatically converted to clean Markdown when inserted - no manual reformatting required.
Mixed teams:
The format switcher allows each agent to choose their preferred format - Markdown, HTML, or plain text per response.
| Component | Version | Note |
|---|---|---|
| osTicket | 1.18.x | Plugin extends ThreadEntryBody class |
| PHP | 7.4+ | Recommended: PHP 8.1+ for best performance |
| jQuery | Any | Included in osTicket |
| Parsedown | 1.7.4 | Bundled with plugin (vendor/) |
markdown-support folder to /include/plugins/ on your osTicket serverFinal path: /path/to/osticket/include/plugins/markdown-support/
cd /path/to/osticket/include/plugins
git clone https://github.com/markus-michalski/osticket-markdown-support.git markdown-support
The plugin automatically configures static asset access (.htaccess update) and creates backups of patched core files.
Tip: The plugin automatically creates a backup of the patched core file. Upon deactivation, the original is restored.
| Setting | Description | Default | When to Change |
|---|---|---|---|
| Enable Markdown Support | Globally enable/disable Markdown | On | For temporary deactivation |
| Default Thread Entry Format | Default format for new entries | Markdown | If HTML or Text preferred |
| Allow Format Switching | Users can switch between formats | On | If format should be locked |
| Auto-Convert Markdown Syntax | Automatically detect Markdown patterns | Off | For API tickets without format parameter |
| Show Live Preview | Display real-time preview pane | On | Disable for performance issues |
| Show Markdown Toolbar | Show formatting buttons | On | If agents are Markdown experts |
"Auto-Convert Markdown Syntax" only works when "Enable Markdown Support" is also active.
For technical teams:
Default Format: Markdown, Format Switching: On, Auto-Convert: Off, Preview: On, Toolbar: On
For API-heavy workflows:
Default Format: Markdown, Auto-Convert: On (if API doesn't send format field)
For mixed teams (Markdown + HTML):
Default Format: HTML, Format Switching: On, Toolbar: On
| Button | Markdown | Shortcut | Description |
|---|---|---|---|
| B | **text** |
Ctrl+B | Bold text |
| I | *text* |
Ctrl+I | Italic text |
| H | ## text |
Ctrl+H | Heading (cycles H1-H6) |
| Link | [text](url) |
Ctrl+K | Insert link |
<> |
`code` |
- | Inline code |
{ } |
```lang |
- | Code block |
| List | - item |
- | Unordered list |
| 1. | 1. item |
- | Ordered list |
| Quote | > quote |
- | Blockquote |
| Line | --- |
- | Horizontal rule |
| Image |  |
- | Upload and insert image |
Images can be inserted into the Markdown editor in three ways:
| Method | Description |
|---|---|
| Drag & Drop | Drag an image file from file manager into the editor |
| Clipboard | Paste a screenshot directly with Ctrl+V |
| Toolbar Button | Click the image icon in the toolbar - opens native file picker |
The plugin uploads the image via osTicket's Draft Attachment API and automatically inserts Markdown image syntax  at the cursor position.
Supported image formats: JPEG, PNG, GIF, WebP, BMP. SVG is blocked for security reasons.
During upload, a placeholder
![Uploading image-1...]()is displayed. Multiple images can be uploaded in parallel.
The plugin seamlessly integrates with osTicket's Canned Response system. When inserting a canned response, the HTML content is automatically converted to clean Markdown if the Markdown editor is active.
Supported conversions:
| HTML Element | Markdown Result |
|---|---|
<h1> to <h6> |
# to ###### |
<strong>, <b> |
**bold** |
<em>, <i> |
*italic* |
<a href="..."> |
[Text](URL) |
<img> |
 |
<blockquote> |
> Quote |
<ul>, <ol> |
- Item / 1. Item |
<pre><code> |
Fenced code block with backticks |
<code> |
`inline code` |
<hr> |
--- |
How it works:
The conversion happens entirely client-side - no additional server requests needed. Existing HTML canned responses do not need to be modified.
"Original Message" and "Last Message" options are also converted correctly -
<blockquote>becomes Markdown blockquotes (> ...).
The live preview pane shows real-time rendering of Markdown content via a server-side API endpoint. Updates automatically with 500ms debouncing.
# Heading 1
## Heading 2
### Heading 3
**Bold text**
*Italic text*
~~Strikethrough~~
`Inline code`
- Unordered list
- Item 2
- Sub-item
1. Ordered list
2. Item 2
```php
<?php
echo "Hello World!";
?>
```
| Column 1 | Column 2 |
|----------|----------|
| Value 1 | Value 2 |

When creating tickets via the osTicket API, use the format field:
POST /api/tickets.json
{
"name": "John Doe",
"email": "john@example.com",
"subject": "Ticket with Markdown",
"message": "## Problem\n\nThe server is **not responding**.\n\n```bash\nping server.example.com\n```",
"format": "markdown"
}
If your API integration doesn't send a format field but contains Markdown syntax:
The plugin uses weighted pattern matching:
| Pattern | Points | Example |
|---|---|---|
| Code blocks | 20 | ```code``` |
| Headings | 15 | # Title |
| Links | 15 | [Text](url) |
| Lists | 12 | - Item |
| Bold/Italic | 10/8 | **bold**, *italic* |
| Inline code | 5 | `code` |
Threshold: 5 points (intentionally low for minimal false-negatives)
| Format Value | Description |
|---|---|
text |
Plain text (no formatting) |
html |
HTML WYSIWYG editor |
markdown |
Markdown editor with live preview |
The plugin uses two osTicket Signals and output buffer injection:
| Signal / Mechanism | Handler | Purpose |
|---|---|---|
object.view |
AssetInjector |
Inject CSS/JS assets into page |
threadentry.created |
ThreadEntryHandler |
Format detection and auto-conversion |
ob_start() callback |
AssetInjector |
CSS before </head>, JS before </body> |
enable() override |
CorePatcher |
Extend ThreadEntryBody via Reflection, patch core file |
disable() override |
CorePatcher |
Restore core file from backup |
Symptoms:
Check:
Fix:
chmod 755 /path/to/osticket/include/plugins/markdown-support
chmod 644 /path/to/osticket/include/plugins/markdown-support/*.php
Symptoms:
Check:
/include/.htaccessFix:
Re-enable the plugin (Disable -> Enable). The plugin automatically updates /include/.htaccess:
<FilesMatch "\.(js|css|map|json|png|jpg|jpeg|gif|svg|ico|woff|woff2|ttf|eot|otf)$">
Require all granted
Allow from all
</FilesMatch>
NGINX users need to configure this manually - see Technical Details.
Symptoms:
Check:
tail -f /path/to/osticket/include/ost-errors.logvendor/erusev/parsedown/Parsedown.php exist?Fix:
Parsedown.php not found -> Re-download plugin from releasesThreadEntryBody class not found -> osTicket 1.18.x requiredReflectionClass not found -> PHP Reflection extension missingSymptoms:
![Uploading image-1...]() remains in textCheck:
upload_max_filesize and post_max_sizeFix:
Symptoms:
<p>, <strong>, <blockquote> tags are visibleCheck:
markdown-editor.js version current? (Check Network tab)Fix:
Symptoms:
Check:
markdown-editor.js loads with 200 OK?typeof jQuery in consoleFix:
Hard refresh the page (Ctrl+Shift+R). The plugin uses MutationObserver for dynamically loaded textareas.
Symptoms:
Check:
Plugin config: Both "Allow Format Switching" and "Enable Markdown Support" must be On.
Fix:
Admin Panel -> Plugins -> Markdown Support -> Configure -> enable both options.
markdown-support/
+-- plugin.php # Plugin metadata
+-- class.MarkdownPlugin.php # Main plugin class
+-- config.php # Admin configuration
+-- markdown-body.php # MarkdownThreadEntryBody class
+-- markdown-preview.php # API endpoint for live preview
+-- MarkdownDetector.php # Markdown syntax detection
+-- TicketFormatHandler.php # Format determination
+-- composer.json # Dependencies
+-- phpunit.xml.dist # PHPUnit configuration
+--
+-- src/ # Service classes (v2.0 refactoring)
| +-- Config/
| | +-- ConfigCache.php # Singleton for configuration
| +-- Core/
| | +-- CorePatcher.php # Reflection + core file patching
| +-- Signal/
| | +-- ThreadEntryHandler.php # Signal handler for entries
| +-- Asset/
| | +-- AssetInjector.php # CSS/JS output buffer injection
| | +-- AssetDeployer.php # Asset deployment + .htaccess
| +-- Format/
| +-- MarkdownSanitizer.php # Unified XSS prevention
| +-- MarkdownDetectorInterface.php
| +-- FormatHandlerInterface.php
+--
+-- css/
| +-- markdown-editor.css # Editor styling (~840 lines)
+-- js/
| +-- markdown-editor.js # Editor JavaScript (~2200 lines)
+-- vendor/
| +-- erusev/parsedown/Parsedown.php # Markdown parser (1.7.4)
+-- tests/
| +-- Unit/ # 77 unit tests
| +-- Integration/ # 24 integration tests
+-- i18n/
+-- de_DE/ # German translation
The HTML-to-Markdown conversion happens client-side in the browser. The built-in converter processes HTML elements recursively via the DOM and produces clean Markdown without external dependencies.
Technical details of the conversion:
document.createElement('div') as a safe DOM parserdocument.createElement('textarea')The v2.0 refactoring split the monolithic plugin class into dedicated services:
| Service | Responsibility |
|---|---|
| ConfigCache | Singleton - caches plugin config statically (solves osTicket issue: signal callbacks receive new plugin instances without config) |
| CorePatcher | Extends ThreadEntryBody::$types via Reflection, patches class.thread.php for Markdown support |
| AssetInjector | Output buffer injection: CSS before </head>, JS deferred before </body> |
| AssetDeployer | .htaccess update, deploys markdown-preview.php to /api/ |
| ThreadEntryHandler | Signal handler: format detection, newline restoration, DB update |
| MarkdownSanitizer | Unified XSS prevention (javascript: URLs, data: URIs, event handlers) |
The plugin extends osTicket's ThreadEntryBody class via PHP Reflection:
// CorePatcher::extendThreadEntryTypes()
$reflection = new ReflectionClass('ThreadEntryBody');
$property = $reflection->getProperty('formats');
$property->setAccessible(true);
$formats = $property->getValue(null);
$formats['markdown'] = 'Markdown';
$property->setValue(null, $formats);
Additionally, class.thread.php is patched to register MarkdownThreadEntryBody as format handler. A backup is created with .markdown-backup suffix and automatically restored upon deactivation.
Layer 1 - Parsedown SafeMode:
Escapes inline HTML in Markdown. <script>, <iframe>, <object> are blocked.
Layer 2 - MarkdownSanitizer:
Removes javascript: URLs, data: URIs, on* event handlers, IE expression() CSS.
Layer 3 - Format::sanitize:
osTicket's standard XSS filter with whitelist-based HTML tag filtering.
Image Upload Security (v2.1):
| Protection | Description |
|---|---|
| MIME type whitelist | Only JPEG, PNG, GIF, WebP, BMP - SVG blocked (XSS vector) |
| URL validation | Server URLs are checked against javascript: and unknown schemes |
| Alt text sanitization | Filenames are stripped of Markdown special characters []() |
| CSRF token check | Upload is aborted if no valid token is present |
| Error response sanitization | HTML is stripped from error messages, length limited to 200 characters |
Tested XSS vectors:
<script> tags<img src=x onerror=alert(1)>[Link](javascript:alert(1))java%09script:)data:text/html;base64,...)Apache is configured automatically. For NGINX, add manually:
location ~* ^/include/plugins/markdown-support/(css|js)/.*\.(css|js|map)$ {
allow all;
access_log off;
expires 30d;
}
101 tests, 296 assertions (PHPUnit 9.6):
| Category | Tests | Focus |
|---|---|---|
| Security | 20 | XSS prevention (critical) |
| Unit | 57 | Rendering, edge cases, config |
| Integration | 24 | Plugin lifecycle, signals, assets |
composer test # Run tests
Q: Does Markdown work in the Client Portal?
A: Markdown-formatted ticket responses render correctly in the Client Portal. However, clients cannot write in Markdown format (staff only).
Q: Can I set Markdown as the default format?
A: Yes, in plugin configuration: Set "Default Thread Entry Format" to Markdown.
Q: What happens if I disable the plugin?
A: Existing Markdown-formatted entries display as raw Markdown text. No data is lost - re-enabling renders them correctly again. The patched core file is restored from backup.
Q: Can I mix Markdown and HTML in the same ticket?
A: Yes, each ticket response can have its own format. The format switcher allows changing per entry.
Q: Do I need to rewrite my canned responses for Markdown?
A: No. Existing HTML canned responses are automatically converted to Markdown when inserted. The originals remain unchanged in the database.
Q: What happens with canned response attachments?
A: Attachments are processed correctly in both modes (Markdown and HTML) and assigned to the ticket draft.
Q: Does the conversion work for "Original Message" and "Last Message"?
A: Yes. These osTicket options return HTML with <blockquote> tags, which are correctly converted to Markdown blockquotes (> ...).
Q: Can I disable the automatic conversion?
A: The conversion is tied to the editor mode. When using HTML mode (Redactor), default behavior is preserved - no conversion happens.
Q: Which image formats are supported?
A: JPEG, PNG, GIF, WebP, and BMP. SVG is blocked for security reasons (XSS vector).
Q: Where are uploaded images stored?
A: Images are stored via osTicket's Draft Attachment system - same mechanism as the HTML editor. They are associated with the ticket draft and automatically linked when the response is submitted.
Q: Can I upload multiple images at once?
A: Yes, with drag & drop and the file dialog, multiple images can be selected simultaneously. Each image gets its own upload indicator.
Q: What happens to the image if I discard the response?
A: The image remains as a draft attachment. osTicket automatically cleans up orphaned draft attachments.
Q: Does the plugin support GitHub-Flavored Markdown (GFM)?
A: Partially. Parsedown supports fenced code blocks, tables, and autolinks. Task lists (- [ ]) and emoji shortcuts (:smile:) are not supported.
Q: Can I use HTML within Markdown?
A: No, for security reasons. Parsedown runs in SafeMode, which blocks inline HTML. Use the HTML format editor for HTML content.
Q: Is syntax highlighting available for code blocks?
A: Not by default. Parsedown generates <pre><code> blocks without highlighting. An external library like Prism.js or Highlight.js can be added.
Q: Does it work with osTicket 1.17 or earlier?
A: No. The plugin requires osTicket 1.18.x for the ThreadEntryBody class structure.
Q: Is it compatible with the API Endpoints Plugin?
A: Yes. The plugins work seamlessly together. format=markdown in API requests is correctly processed.
Q: Is it compatible with PHP 8.x?
A: Yes. The plugin is tested with PHP 7.4, 8.0, 8.1, 8.2, and 8.3. The CI pipeline tests all versions.
Q: Does the plugin affect loading performance?
A: Minimally. Markdown parsing is very fast (~1-2ms per entry). No rendering cache needed as Parsedown is fast enough.
Q: How large can Markdown entries be?
A: Parsedown has no size limit. osTicket's DB limit for ost_thread_entry.body is MEDIUMTEXT (16 MB).
This plugin is released under the GNU General Public License v2, compatible with osTicket core.
See LICENSE for details.
For questions or issues, please create an issue on GitHub:
Issue Tracker: https://github.com/markus-michalski/osticket-markdown-support/issues
When reporting issues, please include:
php -v)Developed by Markus Michalski
Contributions welcome!
See CHANGELOG.md for version history.