diff --git a/include/class.api.php b/include/class.api.php index 76fb481dec6cc5b81a0ffb3a3295c73916179da9..67f2855274dac367ccc9158b2a480d08fa6596b5 100644 --- a/include/class.api.php +++ b/include/class.api.php @@ -323,6 +323,17 @@ class ApiXmlDataParser extends XmlDataParser { $value = (bool)$value; } else if ($key == "autorespond") { $value = (bool)$value; + } else if ($key == "message") { + if (!is_array($value)) { + $value = array( + "body" => $value, + "type" => "text/plain", + # TODO: Get encoding from root <xml> node + ); + } else { + $value["body"] = $value[":text"]; + unset($value[":text"]); + } } else if ($key == "attachments") { if(!isset($value['file'][':text'])) $value = $value['file']; @@ -363,34 +374,24 @@ class ApiJsonDataParser extends JsonDataParser { $value = (bool)$value; } else if ($key == "autorespond") { $value = (bool)$value; + } elseif ($key == "message") { + // Allow message specified in RFC 2397 format + $data = Format::parseRfc2397($value, 'utf-8'); + if (!isset($data['type']) || $data['type'] != 'text/html') + $value = sprintf('<div style="white-space:pre-wrap">%s</div>', + Format::htmlchars($data['data'])); + else + $value = $data['data']; } else if ($key == "attachments") { foreach ($value as &$info) { $data = reset($info); # PHP5: fopen("data://$data[5:]"); - if (substr($data, 0, 5) != "data:") { - $info = array( - "data" => $data, - "type" => "text/plain", - "name" => key($info)); - } else { - $data = substr($data,5); - list($meta, $contents) = explode(",", $data); - list($type, $extra) = explode(";", $meta); - $info = array( - "data" => $contents, - "type" => ($type) ? $type : "text/plain", - "name" => key($info)); - # XXX: Handle decoding here?? - if (substr($extra, -6) == "base64") - $info["encoding"] = "base64"; - # Handle 'charset' hint in $extra, such as - # data:text/plain;charset=iso-8859-1,Blah - # Convert to utf-8 since it's the encoding scheme - # for the database. Otherwise, assume utf-8 - list($param,$charset) = explode('=', $extra); - if ($param == 'charset' && $charset) - $contents = Format::utf8encode($contents, $charset); - } + $contents = Format::parseRfc2397($data, 'utf-8', false); + $info = array( + "data" => $contents['data'], + "type" => $contents['type'], + "name" => key($info), + ); } unset($value); } @@ -399,10 +400,6 @@ class ApiJsonDataParser extends JsonDataParser { } } - if(isset($current['message']) && $current['message']) - $current['message'] = sprintf('<div style="white-space:pre-wrap">%s</div>', - Format::htmlchars($current['message'])); - return $current; } } diff --git a/include/class.format.php b/include/class.format.php index 11f0b1040b7e5a8e36f3ba7b65b92cf326dfe21e..843033ae205875af379cff31c7c26026df1a2855 100644 --- a/include/class.format.php +++ b/include/class.format.php @@ -369,5 +369,62 @@ class Format { return (empty($text)) ? 'n-a' : $text; } + /** + * Parse RFC 2397 formatted data strings. Format according to the RFC + * should look something like: + * + * data:[type/subtype][;charset=utf-8][;base64],data + * + * Parameters: + * $data - (string) RFC2397 formatted data string + * $output_encoding - (string:optional) Character set the input data + * should be encoded to. + * $always_convert - (bool|default:true) If the input data string does + * not specify an input encding, assume iso-8859-1. If this flag is + * set, the output will always be transcoded to the declared + * output_encoding, if set. + * + * Returs: + * array (data=>parsed and transcoded data string, type=>MIME type + * declared in the data string or text/plain otherwise) + * + * References: + * http://www.ietf.org/rfc/rfc2397.txt + */ + function parseRfc2397($data, $output_encoding=false, $always_convert=true) { + if (substr($data, 0, 5) != "data:") + return array('data'=>$data, 'type'=>'text/plain'); + + $data = substr($data, 5); + list($meta, $contents) = explode(",", $data, 2); + if ($meta) + list($type, $extra) = explode(";", $meta, 2); + else + $extra = ''; + if (!isset($type) || !$type) + $type = 'text/plain'; + + $parameters = explode(";", $extra); + + # Handle 'charset' hint in $extra, such as + # data:text/plain;charset=iso-8859-1,Blah + # Convert to utf-8 since it's the encoding scheme for the database. + $charset = ($always_convert) ? 'iso-8859-1' : false; + foreach ($parameters as $p) { + list($param, $value) = explode('=', $extra); + if ($param == 'charset') + $charset = $value; + elseif ($param == 'base64') + $contents = base64_decode($contents); + } + if ($output_encoding && $charset) + $contents = Format::encode($contents, $charset, $output_encoding); + + return array( + 'data' => $contents, + 'type' => $type + ); + } + } ?> diff --git a/include/class.forms.php b/include/class.forms.php index 48e1e9047156e384d3324edc82fcd36add8520a7..b51629bc5c69fcca17a905e5916b58dd349e4f6a 100644 --- a/include/class.forms.php +++ b/include/class.forms.php @@ -553,6 +553,14 @@ class ChoiceField extends FormField { ); } + function parse($value) { + if (is_numeric($value)) + return $value; + foreach ($this->getChoices() as $k=>$v) + if (strcasecmp($value, $v) === 0) + return $k; + } + function toString($value) { $choices = $this->getChoices(); if (isset($choices[$value])) diff --git a/include/class.ticket.php b/include/class.ticket.php index d6002ab73f7e0598fb9690a571d8be75716d6857..82cc169a59eb019fe6c2d87a61f1052561f66586 100644 --- a/include/class.ticket.php +++ b/include/class.ticket.php @@ -1897,7 +1897,7 @@ class Ticket { foreach ($form->getFields() as $field) { $fname = $field->get('name'); if ($fname && isset($vars[$fname]) && !$field->value) - $field->value = $vars[$fname]; + $field->value = $field->parse($vars[$fname]); } // Don't enforce form validation for email diff --git a/setup/doc/api/tickets.md b/setup/doc/api/tickets.md index a8bfd39f1b0791d9bb097484d0d09364e1ef1749..9c6f3dfe4af5c1d22a15ab1c9f54d2a8cd662c9f 100644 --- a/setup/doc/api/tickets.md +++ b/setup/doc/api/tickets.md @@ -17,13 +17,18 @@ request content. * __email__: *required* Email address of the submitter * __name__: *required* Name of the submitter * __subject__: *required* Subject of the ticket -* __message__: *required* Initial message for the ticket thread +* __message__: *required* Initial message for the ticket thread. The + message content can be specified using RFC 2397 in the JSON + format. The content of the message element should be the + message body. Encoding is assumed based on the encoding + attributed set in the xml processing instruction. + * __type__: Content-Type of the message body. Valid values are + `text/html` and `text/plain`. If not specified, `text/plain` is + assumed * __alert__: If unset, disable alerts to staff. Default is `true` * __autorespond__: If unset, disable autoresponses. Default is `true` * __ip__: IP address of the submitter -* __phone__: Phone number of the submitter -* __phone_ext__: Phone number extension -- can also be embedded in *phone* -* __priorityId__: Priority *id* for the new ticket to assume +* __priority__: Priority *id* for the new ticket to assume * __source__: Source of the ticket, default is `API` * __topicId__: Help topic *id* associated with the ticket * __attachments__: An array of files to attach to the initial message. @@ -34,6 +39,47 @@ request content. * __type__: Mime type of the file. Default is `text/plain` * __encoding__: Set to `base64` if content is base64 encoded +### Custom Forms and Fields ### + +Fields added to both the common ticket and common client forms can accept +data via the API as well. The `name` attribute given in form designer is +used to match up the data in the API request with the field to parse and +receive the data. + +These fields are included with the stock common client form. The `name` +field can be changed and the field can also be deleted. This section is +provided as an initial list. The fields supported by your help desk will +depend on your configuration for the common client and common ticket forms. + +* __phone__: Phone number of the submitter. If the extension is + included, use a capital 'X' followed by the extension +* __notes__: Internal notes for the client created. These notes are + only visible to authenticated staff members + +#### Field Data Processing Rules #### + +Some fields have special formatting for inputs. For instance date fields +must receive the date formatted in Unix epoch time. This processing is +performed automatically for requests from the web interface. However, in the +case of the API, the remote requester is required to perform the procecssing +priori to submitting the request via the API. The sections below outline how +to format data for custom fields for requests sent via the API. + +* __Date Fields__: Date fields need to be formatted in a format + recognizable to PHP's strtotime() function. The best formats are + [Unix epoch time](wikipedia.org/wiki/Unix_time), and [ISO 8601] + (http://xkcd.com/1179/). If using timezone offset in the date + specification, ensure that the _Timezone Aware_ flag is set for the + field. + +* __Phone number Fields__: If the extension is provided with the phone + number, include a capital 'X' between the phone number and phone + extension parts of the field. + +* __Choice and Selection Fields__: Use either the key specified with the + choice field, or the name of the field. For selection fields, do not + use anything from the `extra` column of the list designer. + ### XML Payload Example ###### * `POST /api/tickets.xml` @@ -55,8 +101,8 @@ Notice that the phone extension can be sent as the `@ext` attribute of the <name>Angry User</name> <email>api@osticket.com</email> <subject>Testing API</subject> - <phone ext="123">318-555-8634</phone> - <message><![CDATA[Message content here]]></message> + <phone>318-555-8634X123</phone> + <message type="text/plain"><![CDATA[Message content here]]></message> <attachments> <file name="file.txt" type="text/plain"><![CDATA[ File content is here and is automatically trimmed @@ -99,7 +145,7 @@ an object or array definition, and newlines are not allowed inside strings. "phone": "3185558634X123", "subject": "Testing API", "ip": "123.211.233.122", - "message": "MESSAGE HERE", + "message": "data:text/html,MESSAGE <b>HERE</b>", "attachments": [ {"file.txt": "data:text/plain;charset=utf-8,content"}, {"image.png": "data:image/png;base64,R0lGODdhMAA..."},