2014Kiosks
TicketCloud
Back

TicketCloud

Powerful support ticket system built as the first app for MiPortal online operating system.

  1. PHP5. jQuery. XML for data exchange.

I know. I KNOW. But hear me out.

The Wild Ambition of MiPortal

MiPortal was insane. The idea was to build an "online operating system" — a web-based platform where businesses could run their entire operation. Think Google Workspace, but if it was built by a college student in the Dominican Republic who had more ambition than experience.

The core concept: every application runs in a "window" within the browser. Multiple windows, draggable, resizable, minimizable. Like Windows 95, but in a browser, and it's 2014 so this was actually kind of impressive.

TicketCloud was the flagship application. If MiPortal was the operating system, TicketCloud was the Microsoft Word — the app that proved the platform worked.

Building a Ticket System From Scratch

No frameworks. No libraries (except jQuery, which was basically oxygen in 2014). Just raw PHP, raw SQL, and a lot of hope.

The ticket lifecycle:

  1. Customer submits a ticket (or emails it in — we had email parsing!)
  2. System auto-assigns based on category rules
  3. Agent works the ticket, adding internal notes and public replies
  4. Resolution, customer feedback, close

Sounds simple. It wasn't.

The Features That Made It Work

Smart Assignment: Tickets went to the right person automatically. Billing issues to billing. Technical issues to tech. The rule engine was surprisingly sophisticated for something a 20-year-old built in a dorm room.

SLA Tracking: Overdue ticket...

Built With

PHP5jQueryJavaScriptXML

Impact

Platform

MiPortal

Type

SaaS

Under the Hood

A peek at the implementation — the kind of code that powers TicketCloud.

TicketHandler.phpphp
1<?php
2// This is ACTUAL 2014-ERA CODE (slightly cleaned up)
3// Don't judge me. We all started somewhere.
4
5class TicketHandler {
6    private $db;
7    
8    public function createTicket($data) {
9        // 2014 me didn't know about prepared statements yet
10        // THIS IS BAD DON'T DO THIS
11        $title = mysql_real_escape_string($data['title']);
12        $body = mysql_real_escape_string($data['body']);
13        $email = mysql_real_escape_string($data['email']);
14        
15        // Auto-assign based on category
16        $category = $this->categorize($title . ' ' . $body);
17        $assignee = $this->getAssigneeForCategory($category);
18        
19        $sql = "INSERT INTO tickets (title, body, email, category, assignee, status, created_at)
20                VALUES ('$title', '$body', '$email', '$category', '$assignee', 'open', NOW())";
21        
22        mysql_query($sql);
23        $ticketId = mysql_insert_id();
24        
25        // Send confirmation email
26        $this->sendConfirmation($email, $ticketId, $title);
27        
28        // Notify the assignee
29        $this->notifyAgent($assignee, $ticketId);
30        
31        // If multi-installation, sync to master
32        if (defined('SYNC_MASTER') && SYNC_MASTER) {
33            $this->syncToMaster($ticketId);
34        }
35        
36        return $ticketId;
37    }
38    
39    // The XML sync that kept me up at night
40    private function syncToMaster($ticketId) {
41        $ticket = $this->getTicket($ticketId);
42        
43        $xml = new SimpleXMLElement('<ticket/>');
44        $xml->addChild('id', $ticket['id']);
45        $xml->addChild('title', htmlspecialchars($ticket['title']));
46        $xml->addChild('body', htmlspecialchars($ticket['body']));
47        $xml->addChild('status', $ticket['status']);
48        $xml->addChild('source_installation', INSTALLATION_ID);
49        
50        $ch = curl_init(MASTER_SYNC_URL);
51        curl_setopt($ch, CURLOPT_POST, true);
52        curl_setopt($ch, CURLOPT_POSTFIELDS, $xml->asXML());
53        curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/xml']);
54        curl_exec($ch);
55        curl_close($ch);
56        
57        // Error handling? What's that?
58        // (2014 me was optimistic)
59    }
60}

Discussion

💬 Be nice, or be funny. Preferably both.