The Plugin Update Checker automatically checks GitHub for available updates of all installed osTicket plugins and shows update badges directly in the plugin list (Admin Panel > Manage > Plugins). No more manually visiting GitHub repositories to check for new versions.
Installed Plugins
Plugin Name (instances) Version Status Date Installed
----------------------------------------------------------------------
Markdown Support (2) 1.0.0 [1.2.0] Enabled 2025-01-10
^^^^^^^ clickable badge
API Endpoints (3) 1.1.0 [1.1.2] Enabled 2025-01-05
Priority Icons (1) 1.0.3 Enabled 2025-01-08
(no badge = up to date)
JSON_HEX_TAG| Component | Version | Note |
|---|---|---|
| osTicket | 1.18.x | Tested with 1.18.x |
| PHP | 8.1+ | Required |
| PHP cURL | any | Required for GitHub API calls |
| Modern Browser | ES6+ | Chrome, Firefox, Edge, Safari |
update-check folder to /include/plugins/ on your osTicket servercomposer install --no-dev in the plugin directoryFinal path: /path/to/osticket/include/plugins/update-check/
cd /path/to/osticket/include/plugins
git clone https://github.com/markus-michalski/osticket-plugin-update-check.git update-check
cd update-check
composer install --no-dev
The plugin automatically creates a singleton instance and starts checking for updates.
include/plugins/update-check/
+-- plugin.php # Plugin metadata
+-- class.UpdateCheckPlugin.php # Main plugin class
+-- config.php # Configuration form
+-- src/
| +-- AssetInjector.php # Output buffer injection
| +-- FileCache.php # File-based cache
| +-- GitHubReleaseChecker.php # GitHub API client
| +-- PluginUpdateCollector.php # Update data collector
+-- assets/
| +-- update-badges.css # Badge styling
| +-- update-badges.js # DOM manipulation
+-- vendor/ # Composer autoload
+-- README.md
+-- CHANGELOG.md
| Setting | Default | Description |
|---|---|---|
| Cache Duration | 6 hours | How long GitHub API responses are cached |
| GitHub Token | (empty) | Optional Personal Access Token for higher rate limits |
| Mode | Limit | Recommendation |
|---|---|---|
| Without token | 60 requests/hour | Fine for fewer than 10 plugins |
| With token | 5,000 requests/hour | Recommended for many plugins |
If you have many plugins or see rate limit errors:
bootstrap() detects if user is on /scp/plugins.phpThe plugin uses PHP's version_compare() with v prefix stripping:
v1.2.0 and 1.2.0 are treated identicallysys_get_temp_dir() . '/osticket-update-check/'expires_at and data fieldsFor a plugin to receive update checks, its plugin.php must contain a GitHub URL in the url field:
<?php return [
'id' => 'vendor:plugin-name',
'version' => '1.0.0',
'name' => 'My Plugin',
'url' => 'https://github.com/user/repo', // Required!
'plugin' => 'class.MyPlugin.php:MyPlugin',
];
Supported URL formats:
https://github.com/user/repohttps://github.com/user/repo.githttp://github.com/user/repoAdditionally required:
v1.0.0 or 1.0.0)Check:
Plugin enabled?
Plugins have GitHub URL?
plugin.php of the target plugin'url' => 'https://github.com/...' entryGitHub Releases exist?
Check PHP error log:
grep "UpdateCheck" /path/to/osticket/include/ost-errors.log
Clear cache:
rm -rf /tmp/osticket-update-check/
Symptoms: Badges stop appearing after many checks.
Fix:
Possible causes:
version field in plugin.php doesn't match the actual installed versionThe plugin follows a clean separation of concerns:
| Class | Responsibility |
|---|---|
UpdateCheckPlugin |
Bootstrap, page detection, orchestration |
UpdateCheckConfig |
Admin configuration form |
FileCache |
File-based caching with TTL |
GitHubReleaseChecker |
GitHub API client, URL parsing, version comparison |
PluginUpdateCollector |
Iterates plugins, collects update data |
AssetInjector |
Output buffer injection of CSS/JS |
The plugin uses output buffer injection (ob_start() callback) - the same proven pattern as the Priority Icons plugin:
ob_start(function (string $buffer) use ($injector, $updateData): string {
try {
return $injector->inject($buffer, $updateData);
} catch (\Throwable $e) {
error_log('[UpdateCheck] Asset injection failed: ' . $e->getMessage());
return $buffer;
}
});
Why ob_start()?
| Method | Problem |
|---|---|
Signal::connect('apps.scp') |
Only fires on Apps tab, not on plugin list |
$ost->addExtraHeader() |
global $ost is NULL during bootstrap() |
| External CSS/JS files | include/.htaccess blocks HTTP access |
ob_start() callback |
Reliable, crash-protected with try-catch |
XSS Prevention:
JSON_HEX_TAG | JSON_HEX_APOS prevents script injectionisValidHttpUrl()) only allows http: and https: protocolsinnerHTML for user-controlled dataSSL Verification:
CURLOPT_SSL_VERIFYPEER = true and CURLOPT_SSL_VERIFYHOST = 2 on all API callsDouble-Injection Protection:
data-plugin="update-check" before injectingCrash Protection:
ob_start() callback prevents white screen on errorsNo Database Modifications:
| Scenario | Impact |
|---|---|
| Non-plugin pages | Zero (early return in bootstrap()) |
| Plugin page, warm cache | < 10ms additional load time |
| Plugin page, cold cache | ~5s per plugin (cURL timeout), cached afterward |
| cURL timeout | 5 seconds per request |
Q: Does the plugin check for its own updates?
A: Yes! Since the plugin itself has a GitHub URL in its plugin.php, it will also check for its own updates.
Q: What happens if GitHub is unreachable?
A: The plugin caches errors for 1 hour and returns gracefully. No badge is shown and no errors are displayed to the user. Errors are logged to the PHP error log.
Q: Does it work with private GitHub repositories?
A: Only if you configure a GitHub Personal Access Token that has access to the private repository. Without a token, only public repositories are accessible.
Q: What happens when I disable the plugin?
A: The badges disappear immediately. No data is modified - the plugin only injects CSS/JS into the page output.
Q: Why are assets loaded inline instead of as external files?
A: osTicket's include/.htaccess contains Deny from all, which blocks HTTP access to all files in the include/ directory. Inline injection bypasses this without modifying core files.
Q: Why file-based cache instead of database?
A: osTicket doesn't provide a general-purpose cache API. File-based caching using sys_get_temp_dir() is simple, requires no database modifications, and works on all systems.
Q: Does it work with NGINX?
A: Yes. Since all assets are injected inline, there are no external file requests that could be blocked by web server configurations.
Q: How does the plugin find which plugins to check?
A: It uses PluginManager::allInstalled() to iterate all installed plugins, then reads each plugin's plugin.php metadata via PluginManager::getInfoForPath() to find the url field. Only URLs matching github.com are checked.
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-plugin-update-check/issues
When reporting issues, please include:
php -v)[UpdateCheck]Developed by Markus Michalski
Contributions welcome!
See CHANGELOG.md for version history.