Architecture & Directory Structure
The Ticket System is built on osTicket, an open-source PHP help desk platform, with extensive custom extensions under the Suma namespace. This document covers the overall architecture, directory layout, key files, and coding conventions.
Platform Architecture
┌─────────────────────────────────────────────────────────────────┐
│ Client Portal │
│ (tickets.rhinogroup.com — open.php, tickets.php) │
├─────────────────────────────────────────────────────────────────┤
│ Staff Control Panel │
│ (tickets.rhinogroup.com/scp/ — agent UI) │
├─────────────────┬───────────────────────────────────────────────┤
│ osTicket Core │ Suma Custom Extensions │
│ (class.ticket, │ (include/custom/, plugins/motherload/) │
│ class.thread, │ Suma\Ticket, Gemini AI, Harvest, │
│ class.user) │ Twilio, Notifications, Budget Lock │
├─────────────────┴───────────────────────────────────────────────┤
│ Database Layer │
│ MySQL (managerhinogroup) — Medoo PDO + db_query() │
├─────────────────────────────────────────────────────────────────┤
│ External Services │
│ Gemini API │ Pinecone │ Algolia │ Harvest │ Twilio │ MS 365 │
└─────────────────────────────────────────────────────────────────┘
Directory Structure
ticket-manager/
├── api/ # External API endpoints (ticket creation, cron)
├── apps/ # osTicket apps
├── assets/ # Static assets (images, fonts)
├── custom/ # Frontend build system (Vite + Gulp + SCSS + JS)
│ ├── src/js/ # ES6 modules (AIAssistant, Algolia, Tickets, etc.)
│ ├── src/scss/ # SCSS source files
│ ├── assets/ # Compiled output (CSS + JS bundles)
│ ├── vite.config.js # Vite configuration
│ ├── gulpfile.js # Gulp task runner
│ └── package.json # Node.js dependencies
├── deploy/ # Database migrations (~20 SQL files)
├── docs/ # Internal documentation (OAuth, Gemini, fixes)
├── emails/ # HTML email templates
├── fixes/ # One-off repair scripts
├── images/ # Static images
├── include/ # Core PHP classes and logic
│ ├── custom/ # Suma namespace extensions
│ │ ├── AI/ # Suma\AI namespace (php-chatbot architecture)
│ │ │ ├── ChatService.php # Main orchestrator (factory, streaming, storage)
│ │ │ ├── ContextService.php # RAG context retrieval (Pinecone, org analysis)
│ │ │ ├── ModelConfig.php # osTicket config bridge for AI settings
│ │ │ ├── OsTicketConversationStorage.php # php-chatbot MemoryStorage adapter
│ │ │ ├── OsTicketGeminiModel.php # Custom Gemini model with SSE streaming
│ │ │ └── PromptBuilder.php # System prompt + context assembly
│ │ ├── class-ticket.php # Suma\Ticket extends \Ticket
│ │ ├── class-ticket-process.php # Custom ticket processing
│ │ ├── class-harvest.php # Harvest API integration
│ │ ├── class-harvest-team.php # Harvest team management
│ │ ├── class-ticket-alerts.php # Custom alert logic
│ │ ├── class-asana.php # Asana integration
│ │ ├── class-organization.php # Organization helpers
│ │ ├── class-analytics-scheduler.php # Analytics scheduling
│ │ ├── trait-algolia.php # Algolia search trait
│ │ └── class.algolia.php # Algolia index management
│ ├── lib/php-chatbot/ # Rumenx\PhpChatbot library (PSR-4 autoloaded)
│ ├── client/ # Client portal templates
│ ├── staff/ # Staff panel templates
│ ├── plugins/ # Plugin system (motherload + others)
│ │ └── motherload/ # Custom signal-based plugin framework
│ ├── koolreports/ # KoolReport analytics classes
│ ├── class.gemini.php # Gemini AI urgency analysis queue processor
│ ├── class.gemini-logger.php # Dedicated Gemini queue file logger
│ ├── class.ai-assistant.php # AI knowledge — Pinecone vectors + embedding ops
│ ├── class.harvest-oauth.php # Per-staff Harvest OAuth2 management
│ ├── class.harvest-time-entry.php # Direct time entry from ticket page
│ ├── class.twilio-sms.php # Twilio SMS alerts with dedup and rate limiting
│ ├── class.staff-notifications.php # Browser notifications
│ ├── class.ticket-generator.php # Bulk ticket generator
│ └── cli/ # CLI tools
├── js/ # Legacy JavaScript
├── css/ # Legacy CSS
├── kb/ # Knowledge base
├── pages/ # CMS pages
├── scp/ # Staff Control Panel pages
│ ├── harvester/ # Harvest integration UI
│ ├── timeline/ # Timeline feature
│ ├── my-queue.php # Algolia-powered personal queue
│ ├── pm.php # PM performance dashboard
│ ├── analytics.php # Analytics dashboard
│ ├── ai-assistant.php # Full-page AI chatbot
│ ├── tools.php # Admin tools (bulk generator)
│ └── tickets.php # Main ticket view/action handler
├── tests/ # Test and diagnostic scripts
├── tools/ # Import tools (Harvest, users, orgs)
├── var/ # Runtime data (logs, sessions)
│ └── logs/api/ # API request/response logs
├── vendor/ # Composer dependencies
└── [root .php files] # Entry points + cron jobs
Key Custom Files
| File | Purpose |
|---|---|
include/custom/AI/ChatService.php | Suma\AI\ChatService — main AI chat orchestrator (factory, streaming, context, storage) |
include/custom/AI/ContextService.php | Suma\AI\ContextService — RAG retrieval, org analysis detection, ticket lookups |
include/custom/AI/ModelConfig.php | Suma\AI\ModelConfig — typed config bridge to ost_config AI settings |
include/custom/AI/OsTicketGeminiModel.php | Suma\AI\OsTicketGeminiModel — SSE streaming Gemini model with fallback chains |
include/custom/AI/OsTicketConversationStorage.php | Suma\AI\OsTicketConversationStorage — maps php-chatbot memory to osTicket tables |
include/custom/AI/PromptBuilder.php | Suma\AI\PromptBuilder — dynamic system prompts with source context |
include/custom/class-ticket.php | Suma\Ticket extends \Ticket — stats queries, budget lock, safeQuery(), documentation checks |
include/custom/class-ticket-process.php | Custom ticket processing helpers |
include/class.gemini.php | Gemini AI urgency analysis queue processor (with staff reply cap) |
include/class.gemini-logger.php | Dedicated file-based logger for Gemini queue (var/logs/gemini/) |
include/class.ai-assistant.php | AI ticket knowledge — Pinecone embeddings, vector search, metadata |
include/class.harvest-oauth.php | Per-staff Harvest OAuth2 token management |
include/class.harvest-time-entry.php | Direct time entry from ticket reply/note forms |
include/class.api-logger.php | File-based API request/response logger (var/logs/api/) |
include/class.twilio-sms.php | Twilio SMS alerts with dedup and rate limiting |
include/class.staff-notifications.php | Browser notification system |
include/class.ticket-generator.php | Admin bulk ticket generator tool |
include/plugins/motherload/plugin.php | Plugin dispatcher (signals → plugin classes) |
scp/tickets.php | Staff ticket view/action handler (loads Suma\Ticket) |
scp/ai-assistant.php | Full-page AI Assistant entry point |
scp/ai-history.php | AI analysis snapshots history page |
scp/admin-toolkit.php | Admin-only quick-action tools (Staff ID 1) |
include/staff/ai-chat-widget.inc.php | Floating AI chat widget (Shadow DOM) |
include/staff/ai-assistant-full.inc.php | Full-page AI Assistant interface |
scp/pm.php | PM performance metrics dashboard |
URL Convention — Ticket Number URLs
All user-visible ticket URLs use ?number={ticket_number} (display number, e.g. 108749), not ?id={ticket_id} (internal DB primary key).
Building URLs
// ✅ Correct — use ticket number
sprintf('tickets.php?number=%s', $ticket->getNumber());
$ticket->getLink(); // returns scp/tickets.php?number=...
// ❌ Wrong — do not use ticket_id in user-visible URLs
sprintf('tickets.php?id=%d', $ticket->getId());
Backward Compatibility
Both scp/tickets.php and tickets.php (client) have 301 redirect logic: if ?id= is received, the ticket is looked up and the user is redirected to ?number={number}. This preserves bookmarks, email links, and stored event data.
Internal AJAX Routes — Still Use ticket_id
AJAX path routes (/ajax.php/tickets/{tid}/...), hash routes (#tickets/{id}/...), hidden form fields (name="id"), and bulk tids[] checkboxes all continue to use ticket_id internally.
Frontend Build System
The frontend lives in the custom/ directory with a dual build system:
Vite (Primary — JS Modules)
cd custom
npm run build # Production build
npm run dev # Development with HMR
Entry points defined in vite.config.js:
- AI Assistant module
- Algolia InstantSearch module
- Ticket interactions module
- Notification system module
Gulp (SCSS Compilation)
cd custom
npx gulp sass # Compile SCSS
npx gulp watch # Watch mode
Key Frontend Dependencies
| Package | Purpose |
|---|---|
algoliasearch | Algolia search client |
instantsearch.js | Algolia UI components |
bootstrap 4.6 | UI framework |
mmenu-js | Mobile navigation |
| TinyMCE 6 | Rich text editor (loaded via CDN) |
PHP 8.4 Compatibility
The codebase has been fully modernized for PHP 8.4. Key requirements:
php.ini Requirements
zend.max_allowed_stack_size=512K
zend.reserved_stack_size=64K
Without these, PHP 8.4's stack overflow detection causes crashes on deeply nested osTicket ORM calls.
Code Modernization Applied
each()replaced withforeach(11 files)create_function()replaced with closuresutf8_encode()replaced withiconv()mcryptreplaced withhash_hmac()+random_bytes()- Nested ternaries wrapped in parentheses
- Non-static method calls fixed (~30+ files)
- Rector modernizations: short arrays, null coalescing,
str_contains(), match expressions
Running Compatibility Checks
# PHPCompatibility check (should return 0 errors)
php vendor\bin\phpcs --report=summary --warning-severity=0 .
# Rector dry-run (should report "Rector is done!")
php vendor\bin\rector --dry-run --no-progress-bar
Constants
Key constants defined in bootstrap.php:
TABLE_PREFIX // 'ost_'
GEMINI_QUEUE_TABLE // TABLE_PREFIX . 'gemini_queue'
INCLUDE_DIR // Absolute path to include/
STAFFINC_DIR // Absolute path to scp/
FORM_ANSWER_TABLE // TABLE_PREFIX . 'form_entry_values'
FORM_ENTRY_TABLE // TABLE_PREFIX . 'form_entry'
Extension Pattern
When adding new functionality:
- Never modify core osTicket files unless absolutely necessary
- Extend via
include/custom/(Suma namespace classes) - Use
include/plugins/motherload/plugins/for event-driven features - Use
include/class.*.phpfor standalone service classes - Place database migrations in
deploy/ - Place test/debug scripts in
tests/ - Place one-off fixes in
fixes/
See Plugin System for details on the motherload signal architecture.