| Property | Value |
|---|---|
| Plugin ID | subticket-manager |
| Name | Subticket Manager |
| Plugin class | SubticketPlugin in class.SubticketPlugin.php |
| Configuration class | SubticketPluginConfig in config.php |
| Instances | Singleton, enable() creates the instance automatically |
| License | GNU GPL v2 (file LICENSE) |
On every page request where osTicket loads the active plugin, bootstrap() runs in this order:
The handler only accepts the ID if it is numeric and greater than 0. On creation it sets ticket_pid with a direct UPDATE, without existence, loop or department checks, and then clears the ID from the session.
subticket-manager/
βββ plugin.php # metadata
βββ class.SubticketPlugin.php # plugin class, lifecycle, signal wiring
βββ config.php # configuration form
βββ queue-decoration.php # ParentTicketDecoration
βββ ajax/
β βββ SubticketController.php # logic behind ajax-subticket.php
β βββ PluginPathResolver.php # helper function for tests
βββ scp-files/ # copied to scp/
β βββ subtickets.php # overview page
β βββ ajax-subticket.php # AJAX entry point
β βββ apps.php # applications overview
βββ js/
β βββ subticket-panel.js # panel interactions
β βββ queue-indicator.js # icons in the queue
βββ src/ # PSR-4, namespace SubticketManager
βββ Asset/AssetDeployer.php # copy and remove the scp files
βββ Config/ConfigCache.php # cache for configuration values
βββ Database/DatabaseService.php # tables, column, indexes, foreign keys
βββ Hierarchy/HierarchyService.php # link, unlink, loop check
βββ Signal/TicketEventHandler.php # signal handlers
βββ UI/PanelRenderer.php # HTML, CSS and script loader of the panel
The relationships themselves live in the osTicket column ost_ticket.ticket_pid. On startup the plugin adds, if not yet present:
| Object | Definition |
|---|---|
Table ost_ticket_hierarchy_metadata |
ticket_id (primary key), auto_close_enabled, inherit_settings, dependency_type, max_children, created, updated |
Table ost_ticket_progress |
parent_id (primary key), total_children, completed_children, in_progress_children, pending_children, last_calculated |
Column ost_ticket.version |
int(11) DEFAULT 0 |
Index idx_ticket_pid |
on ost_ticket(ticket_pid) |
Index idx_ticket_hierarchy |
on ost_ticket(ticket_id, ticket_pid, status_id) |
Foreign keys fk_hierarchy_ticket, fk_progress_ticket |
on ost_ticket(ticket_id) with ON DELETE CASCADE, only if the table has no foreign key yet |
The two tables and the
versioncolumn are created by the plugin but never read or written. The plugin's features work withticket_pidalone.
The checks run on every plugin start (that is, on every osTicket page request): as SHOW queries for tables, column and indexes, and per table as a SELECT on information_schema.TABLE_CONSTRAINTS for the foreign key. On uninstall the plugin removes the objects only if Remove Data on Uninstall is enabled (see Configuration).
The AssetDeployer copies scp-files/subtickets.php, scp-files/ajax-subticket.php and scp-files/apps.php to scp/. It runs on enable() and on every start when the file scp/.subticket-deployed-version is missing or contains a different version than plugin.php. The target directory and write permission checks produce the error messages listed under Installation.
| Area | Behavior |
|---|---|
| Linking | ID greater than 0, no self-reference, both tickets must exist, no loop |
| Loop check | isDescendant() follows the ticket_pid chain for at most ten levels. For deeper chains the check ends without a hit. There is no limit on hierarchy depth |
| Multiple parents | Not possible, a ticket has exactly one ticket_pid column |
| Input | Ticket IDs are validated as positive numbers, SQL values go through db_input() |
| CSRF | link, unlink and create require a valid osTicket CSRF token |
| Department access | Checked via canAccessDept(), administrators are exempt |
| Rate limit | 100 requests per 60 seconds per session for children, link, unlink, create |
| Panel output | Number, subject and status are output with htmlspecialchars |
The plugin class contains isCompatible(). The method returns false if the osTicket version is below 1.18.0, PHP is below 7.4 or the column ticket_pid is missing in ost_ticket.
ost_ is hard-coded in all SQL queries.children action and the overview page do not check department permissions.