-
Jared Hancock authored
*This is a major redesign / rework of the osTicket base* This patch drops the concept of static ticket metadata and allows for an admin-configurable arbitrary data that is attachable to tickets The system is architected such that the base osTicket install now comes with a "default" form that has fields for subject, name, email, and phone number. This form is editable to allow for the addition of arbitrary other fields; however, the basic fields must remain in order to be associated with a help-topic and attached to a ticket. This concept can be expanded to allow for arbitrary data associated with registered clients or ticket thread items. Forms are comprised of sections. Sections have a title and instructions properties and a list of fields. Fields have various implementations to represent different data such as text, long answer, phone number, datetime, yes/no, and selections, and are configurable to define the look and feel and interpretation of the respective form field. Dropdown lists are represented as "Dynamic Lists", which are admin-configurable lists of items. Dropdowns can be optionally represented as Bootstrap typeahead fields. This also adds the start of a simple ORM which will hopefully be expanded in the future to support multiple database platforms. Currently, only MySQL is implemented.
Jared Hancock authored*This is a major redesign / rework of the osTicket base* This patch drops the concept of static ticket metadata and allows for an admin-configurable arbitrary data that is attachable to tickets The system is architected such that the base osTicket install now comes with a "default" form that has fields for subject, name, email, and phone number. This form is editable to allow for the addition of arbitrary other fields; however, the basic fields must remain in order to be associated with a help-topic and attached to a ticket. This concept can be expanded to allow for arbitrary data associated with registered clients or ticket thread items. Forms are comprised of sections. Sections have a title and instructions properties and a list of fields. Fields have various implementations to represent different data such as text, long answer, phone number, datetime, yes/no, and selections, and are configurable to define the look and feel and interpretation of the respective form field. Dropdown lists are represented as "Dynamic Lists", which are admin-configurable lists of items. Dropdowns can be optionally represented as Bootstrap typeahead fields. This also adds the start of a simple ORM which will hopefully be expanded in the future to support multiple database platforms. Currently, only MySQL is implemented.
api.tickets.php 6.94 KiB
<?php
include_once INCLUDE_DIR.'class.api.php';
include_once INCLUDE_DIR.'class.ticket.php';
class TicketApiController extends ApiController {
# Supported arguments -- anything else is an error. These items will be
# inspected _after_ the fixup() method of the ApiXxxDataParser classes
# so that all supported input formats should be supported
function getRequestStructure($format, $data=null) {
$supported = array(
"alert", "autorespond", "source", "topicId",
"attachments" => array("*" =>
array("name", "type", "data", "encoding")
),
"message", "ip", "priorityId"
);
# Fetch dynamic form field names for the given help topic and add
# the names to the supported request structure
if (isset($data['topicId'])) {
$topic=Topic::lookup($data['topicId']);
$formset=DynamicFormset::lookup($topic->ht['formset_id']);
foreach ($formset->getForms() as $form)
foreach ($form->getForm()->getFields() as $field)
$supported[] = $field->get('name');
}
if(!strcasecmp($format, 'email')) {
$supported = array_merge($supported, array('header', 'mid',
'emailId', 'ticketId', 'reply-to', 'reply-to-name',
'in-reply-to', 'references'));
$supported['attachments']['*'][] = 'cid';
}
return $supported;
}
/*
Validate data - overwrites parent's validator for additional validations.
*/
function validate(&$data, $format) {
global $ost;
//Call parent to Validate the structure
if(!parent::validate($data, $format))
$this->exerr(400, 'Unexpected or invalid data received');
//Nuke attachments IF API files are not allowed.
if(!$ost->getConfig()->allowAPIAttachments())
$data['attachments'] = array();
//Validate attachments: Do error checking... soft fail - set the error and pass on the request.
if($data['attachments'] && is_array($data['attachments'])) {
foreach($data['attachments'] as &$attachment) {
if(!$ost->isFileTypeAllowed($attachment))
$attachment['error'] = 'Invalid file type (ext) for '.Format::htmlchars($attachment['name']);
elseif ($attachment['encoding'] && !strcasecmp($attachment['encoding'], 'base64')) {
if(!($attachment['data'] = base64_decode($attachment['data'], true)))
$attachment['error'] = sprintf('%s: Poorly encoded base64 data', Format::htmlchars($attachment['name']));
}
}
unset($attachment);
}
return true;
}
function create($format) {
if(!($key=$this->requireApiKey()) || !$key->canCreateTickets())
return $this->exerr(401, 'API key not authorized');
$ticket = null;
if(!strcasecmp($format, 'email')) {
# Handle remote piped emails - could be a reply...etc.
$ticket = $this->processEmail();
} else {
# Parse request body
$ticket = $this->createTicket($this->getRequest($format));
}
if(!$ticket)
return $this->exerr(500, "Unable to create new ticket: unknown error");
$this->response(201, $ticket->getExtId());
}
/* private helper functions */
function createTicket($data) {
# Pull off some meta-data
$alert = $data['alert'] ? $data['alert'] : true;
$autorespond = $data['autorespond'] ? $data['autorespond'] : true;
$data['source'] = $data['source'] ? $data['source'] : 'API';
# Create the ticket with the data (attempt to anyway)
$errors = array();
$topic=Topic::lookup($data['topicId']);
$forms=DynamicFormset::lookup($topic->ht['formset_id'])->getForms();
foreach ($forms as $idx=>$f) {
$forms[$idx] = $form = $f->getForm()->instanciate($f->sort);
# Collect name, email address, and subject for banning and such
foreach ($form->getFields() as $field) {
$fname = $field->get('name');
if ($fname && isset($data[$fname]))
$field->value = $data[$fname];
}
if (!$form->isValid())
$errors = array_merge($errors, $form->errors());
}
$ticket = Ticket::create($data, $errors, $data['source'], $autorespond, $alert);
# Return errors (?)
if (count($errors)) {
if(isset($errors['errno']) && $errors['errno'] == 403)
return $this->exerr(403, 'Ticket denied');
else
return $this->exerr(
400,
"Unable to create new ticket: validation errors:\n"
.Format::array_implode(": ", "\n", $errors)
);
} elseif (!$ticket) {
return $this->exerr(500, "Unable to create new ticket: unknown error");
}
# Save dynamic forms
foreach ($forms as $f) {
$f->set('ticket_id', $ticket->getId());
$f->save();
}
return $ticket;
}
function processEmail() {
$data = $this->getEmailRequest();
if($data['ticketId'] && ($ticket=Ticket::lookup($data['ticketId']))) {
if(($msgid=$ticket->postMessage($data, 'Email')))
return $ticket;
}
if (($thread = ThreadEntry::lookupByEmailHeaders($data))
&& $thread->postEmail($data)) {
return $thread->getTicket();
}
return $this->createTicket($data);
}
}
//Local email piping controller - no API key required!
class PipeApiController extends TicketApiController {
//Overwrite grandparent's (ApiController) response method.
function response($code, $resp) {
//Use postfix exit codes - instead of HTTP
switch($code) {
case 201: //Success
$exitcode = 0;
break;
case 400:
$exitcode = 66;
break;
case 401: /* permission denied */
case 403:
$exitcode = 77;
break;
case 415:
case 416:
case 417:
case 501:
$exitcode = 65;
break;
case 503:
$exitcode = 69;
break;
case 500: //Server error.
default: //Temp (unknown) failure - retry
$exitcode = 75;
}
//echo "$code ($exitcode):$resp";
//We're simply exiting - MTA will take care of the rest based on exit code!
exit($exitcode);
}
function process() {
$pipe = new PipeApiController();
if(($ticket=$pipe->processEmail()))
return $pipe->response(201, $ticket->getNumber());
return $pipe->exerr(416, 'Request failed - retry again!');
}
}
?>