TicketCloud
Powerful support ticket system built as the first app for MiPortal online operating system.
- 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:
- Customer submits a ticket (or emails it in — we had email parsing!)
- System auto-assigns based on category rules
- Agent works the ticket, adding internal notes and public replies
- 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
Impact
Platform
MiPortal
Type
SaaS
Under the Hood
A peek at the implementation — the kind of code that powers TicketCloud.
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}