Description
This ASP script provides a few web pages suitable for viewing tickets on mobile devices. Each one is a mailto link, so you can use Tickets Anywhere commands to manage them.
I packaged it as one script so deployment is easy. Change the 4 variables right at the beginning (userName, password, URI, and helpdeskEmail), and put it in an IIS virtual directory. Then turn on classic ASP in IIS. Secure the directory using another mechanism your phone supports.
I only tested it on my BlackBerry Curve 8330. I know there’s other work being done for mobile help desk, but my phone’s browser isn’t ready for anything that looks pretty.
I’m open for feature requests, if anyone has some.
Source Code
<%@ LANGUAGE="JSCRIPT" %>
<%
// Mobile ticketing system for Spiceworks
// Serve via IIS - make sure classic ASP is enabled
//--------------------------------------------------------------------------
var userName = 'A SPICEWORKS ADMIN EMAIL';
var password = 'ADMIN PASSWORD';
var URI = 'http://YOUR.SPICEWORKS.URL'; // no trailing slash, but include port if non-standard
var helpdeskEmail = 'helpdesk@acme.com'; // where your Tickets Anywhere commands should go
//--------------------------------------------------------------------------
// http://stackoverflow.com/questions/933553/system-net-httpwebrequest-in-classic-asp
/*
Class: HttpRequest
Object encapsulates the process of making an HTTP Request.
Parameters:
url - The target url
data - Any paramaters which are required by the request.
method - Whether to send the request as POST or GET
options - async (true|false): should we send this asyncronously (fire and forget) or should we wait and return the data we get back? Default is false
Returns:
Returns the result of the request and the response headers in text format.
*/
var HttpRequest = function( url, data, method, options )
{
options = options ? options : { "async" : false };
options[ "async" ] = options["async"] ? true : false;
var text = "";
data = data ? data : "";
method = method ? String( method ).toUpperCase() : "POST";
// Make the request
var objXmlHttp = new ActiveXObject( "MSXML2.ServerXMLHTTP" );
objXmlHttp.setOption( 2, 13056 ); // Ignore all SSL errors
try {
objXmlHttp.open( method, url, options[ "async" ] ); // Method, URL, Async?
}
catch (e)
{
text = "Open operation failed: " + e.description;
}
// Timeouts in ms for parts of communication: resolve, connect, send (per packet), receive (per packet)
objXmlHttp.setTimeouts( 30000, 30000, 30000, 30000 );
try {
if ( method == "POST" ) {
objXmlHttp.setRequestHeader( "Content-Type", "application/x-www-form-urlencoded" );
}
if(options["cookie"]) {
objXmlHttp.setRequestHeader( "Cookie", options[ "cookie" ]);
}
if(options[ "accept" ]) {
objXmlHttp.setRequestHeader( "Accept", options[ "accept" ]);
}
objXmlHttp.send( data );
if ( options[ "async" ] ) {
return "";
}
text = objXmlHttp.responseText;
} catch(e) {
text = "Send data failed: " + e.description;
}
try {
// Did we get a "200 OK" status?
if ( objXmlHttp.status != 200 )
{
// Non-OK HTTP response
text = "Http Error: " + objXmlHttp.status + " " + Server.HtmlEncode(objXmlHttp.statusText) + "\n Failed to grab page data from: " + url;
}
} catch(e) {
Response.Write('Possible incorrect URL entered: ' + e.description);
Response.End();
}
var response = {
headers: objXmlHttp.getAllResponseHeaders(),
body: text
};
objXmlHttp = null; // Be nice to the server
return response;
}
function getCookie(headers) {
var rx = new RegExp('Set-Cookie: (.*?);');
var matches = rx.exec(headers);
if(!matches.length){
return null;
}
return matches[1];
}
// Login with the username and password, and return the cookie
function login(site, userName, password) {
var data = 'user[email]=' + Server.URLEncode(userName)
+ '&user[password]=' + Server.URLEncode(password);
var res = HttpRequest(site + '/account/login', data, 'POST');
return getCookie(res.headers);
}
var command = Server.HTMLEncode(Request.QueryString('cmd'));
switch(command) {
case 'unassigned_tickets': showTickets('unassigned');break;
case 'open_tickets': showTickets('open');break;
default: showIndex();
};
function showIndex() {
%>
<a href='?cmd=open_tickets'>open tickets</a><br />
<a href='?cmd=unassigned_tickets'>unassigned tickets</a>
<p><strong>Tickets Anywhere Quick Ref</strong></p>
<dl>
<dd>#accept</dd>
<dd>#add 5m, #add 2h</dd>
<dd>#assign bob</dd>
<dd>#cc</dd>
<dd>#close</dd>
<dd>#due tomorrow, #due in 1 week</dd>
<dd>#mute, #mute off</dd>
<dd>#pri high</dd>
</dl>
<%
}
// Secondary request to get user names, because the service doesn't join tables
function getUserName(id, cookie) {
if(!id) {
return '';
}
var requestOpts = {
cookie: cookie,
accept: 'text/javascript'
}
var res = HttpRequest(URI + '/api/users/' + id + '.json', null, 'GET', requestOpts);
var user = eval('('+res.body+')');
return ' - ' + user.first_name + ' ' + user.last_name;
}
// Ticket fields (SW 4.5)
//status,requires_purchase,updated_at,options,category,assigned_to,ticketable_id,viewed_at,created_by,time_to_resolve,status_updated_at,priority,
//external_id,email_message_id,id,time_spent,summary,reopened,description,closed_at,due_date,ticketable_type,warning_alert_count,
//error_alert_count,created_at
function showTickets(filter) {
var cookie = login(URI, userName, password);
var requestOpts = {
cookie: cookie,
accept: 'text/javascript'
}
var res = HttpRequest(URI + '/api/tickets.json?filter=' + filter, null, 'GET', requestOpts);
var tickets;
try {
tickets = eval('('+res.body+')');
} catch(e) {
Response.Write('Check your username and password. Didn\'t get valid JSON: ' + res.body);
}
%>
<head>
<script language=javascript>
function toggle(id) {
var element = document.getElementById(id);
if(element.style.display=='none') {
element.style.display='';
} else {
element.style.display='none';
}
return false;
}
// Doing this backwards because my BB 8330 doesn't support dynamic HTML
// so I show it all then hide on window load for better phones than mine
function hideDetails()
{
for(var i=0;i<<%= tickets.length %>;i++) {
var element = document.getElementById('details_' + i);
element.style.display = 'none';
}
}
window.onload = hideDetails;
</script>
<style>
body {
font: tahoma non-serif;
}
div.details {
display: block;
background: #EEE;
}
div.details a {
font: smaller;
}
div.bottom_index {
margin-top: 10px;
}
</style>
<%
Response.Write('<title>' + filter + ' tickets</title></head><body>');
Response.Write('<div><a href="' + Request.ServerVariables("SCRIPT_NAME") + '">return to index</a></div>');
for(var i = 0; i < tickets.length; i++) {
Response.Write('<div class="summary"><a href="#" onClick="toggle(\'details_' + i + '\');">↓</a> ' +
'<a href="mailto:' + helpdeskEmail + '?subject=[Ticket #' + tickets[i].id + ']">' + tickets[i].summary +
'</a>' + getUserName(tickets[i].assigned_to, cookie) + '</div>');
Response.Write('<div class="details" id="details_' + i + '"><pre>' + tickets[i].description + '</pre><br />' +
'<a href="#" onClick="toggle(\'details_' + i + '\');">close details</a></div>');
}
Response.Write('<div class="bottom_index"><a href="' + Request.ServerVariables("SCRIPT_NAME") + '">return to index</a></div></body>');
}
%>