Skip to main content

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

FilePurpose
include/custom/AI/ChatService.phpSuma\AI\ChatService — main AI chat orchestrator (factory, streaming, context, storage)
include/custom/AI/ContextService.phpSuma\AI\ContextService — RAG retrieval, org analysis detection, ticket lookups
include/custom/AI/ModelConfig.phpSuma\AI\ModelConfig — typed config bridge to ost_config AI settings
include/custom/AI/OsTicketGeminiModel.phpSuma\AI\OsTicketGeminiModel — SSE streaming Gemini model with fallback chains
include/custom/AI/OsTicketConversationStorage.phpSuma\AI\OsTicketConversationStorage — maps php-chatbot memory to osTicket tables
include/custom/AI/PromptBuilder.phpSuma\AI\PromptBuilder — dynamic system prompts with source context
include/custom/class-ticket.phpSuma\Ticket extends \Ticket — stats queries, budget lock, safeQuery(), documentation checks
include/custom/class-ticket-process.phpCustom ticket processing helpers
include/class.gemini.phpGemini AI urgency analysis queue processor (with staff reply cap)
include/class.gemini-logger.phpDedicated file-based logger for Gemini queue (var/logs/gemini/)
include/class.ai-assistant.phpAI ticket knowledge — Pinecone embeddings, vector search, metadata
include/class.harvest-oauth.phpPer-staff Harvest OAuth2 token management
include/class.harvest-time-entry.phpDirect time entry from ticket reply/note forms
include/class.api-logger.phpFile-based API request/response logger (var/logs/api/)
include/class.twilio-sms.phpTwilio SMS alerts with dedup and rate limiting
include/class.staff-notifications.phpBrowser notification system
include/class.ticket-generator.phpAdmin bulk ticket generator tool
include/plugins/motherload/plugin.phpPlugin dispatcher (signals → plugin classes)
scp/tickets.phpStaff ticket view/action handler (loads Suma\Ticket)
scp/ai-assistant.phpFull-page AI Assistant entry point
scp/ai-history.phpAI analysis snapshots history page
scp/admin-toolkit.phpAdmin-only quick-action tools (Staff ID 1)
include/staff/ai-chat-widget.inc.phpFloating AI chat widget (Shadow DOM)
include/staff/ai-assistant-full.inc.phpFull-page AI Assistant interface
scp/pm.phpPM 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

PackagePurpose
algoliasearchAlgolia search client
instantsearch.jsAlgolia UI components
bootstrap 4.6UI framework
mmenu-jsMobile navigation
TinyMCE 6Rich 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 with foreach (11 files)
  • create_function() replaced with closures
  • utf8_encode() replaced with iconv()
  • mcrypt replaced with hash_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:

  1. Never modify core osTicket files unless absolutely necessary
  2. Extend via include/custom/ (Suma namespace classes)
  3. Use include/plugins/motherload/plugins/ for event-driven features
  4. Use include/class.*.php for standalone service classes
  5. Place database migrations in deploy/
  6. Place test/debug scripts in tests/
  7. Place one-off fixes in fixes/

See Plugin System for details on the motherload signal architecture.