-
Jared Hancock authoredf7384359
ajax.tickets.php 17.21 KiB
<?php
/*********************************************************************
ajax.tickets.php
AJAX interface for tickets
Peter Rotich <peter@osticket.com>
Copyright (c) 2006-2013 osTicket
http://www.osticket.com
Released under the GNU General Public License WITHOUT ANY WARRANTY.
See LICENSE.TXT for details.
vim: expandtab sw=4 ts=4 sts=4:
**********************************************************************/
if(!defined('INCLUDE_DIR')) die('403');
include_once(INCLUDE_DIR.'class.ticket.php');
require_once(INCLUDE_DIR.'class.ajax.php');
class TicketsAjaxAPI extends AjaxController {
function lookup() {
global $thisstaff;
if(!is_numeric($_REQUEST['q']))
return self::lookupByEmail();
$limit = isset($_REQUEST['limit']) ? (int) $_REQUEST['limit']:25;
$tickets=array();
$sql='SELECT DISTINCT ticketID, email.address AS email'
.' FROM '.TICKET_TABLE.' ticket'
.' LEFT JOIN '.USER_TABLE.' user ON user.id = ticket.user_id'
.' LEFT JOIN '.USER_EMAIL_TABLE.' email ON user.id = email.user_id'
.' WHERE ticketID LIKE \''.db_input($_REQUEST['q'], false).'%\'';
$sql.=' AND ( staff_id='.db_input($thisstaff->getId());
if(($teams=$thisstaff->getTeams()) && count(array_filter($teams)))
$sql.=' OR team_id IN('.implode(',', db_input(array_filter($teams))).')';
if(!$thisstaff->showAssignedOnly() && ($depts=$thisstaff->getDepts()))
$sql.=' OR dept_id IN ('.implode(',', db_input($depts)).')';
$sql.=' ) '
.' ORDER BY ticket.created LIMIT '.$limit;
if(($res=db_query($sql)) && db_num_rows($res)) {
while(list($id, $email)=db_fetch_row($res)) {
$info = "$id - $email";
$tickets[] = array('id'=>$id, 'email'=>$email, 'value'=>$id,
'info'=>$info, 'matches'=>$_REQUEST['q']);
}
}
if (!$tickets)
return self::lookupByEmail();
return $this->json_encode($tickets);
}
function lookupByEmail() {
global $thisstaff;
$limit = isset($_REQUEST['limit']) ? (int) $_REQUEST['limit']:25;
$tickets=array();
$sql='SELECT email.address AS email, count(ticket.ticket_id) as tickets '
.' FROM '.TICKET_TABLE.' ticket'
.' JOIN '.USER_TABLE.' user ON user.id = ticket.user_id'
.' JOIN '.USER_EMAIL_TABLE.' email ON user.id = email.user_id'
.' LEFT JOIN '.FORM_ENTRY_TABLE.' entry ON (entry.object_id = user.id
AND entry.object_type=\'U\')
LEFT JOIN '.FORM_ANSWER_TABLE.' data ON (data.entry_id = entry.id)'
.' WHERE (email.address LIKE \'%'.db_input(strtolower($_REQUEST['q']), false).'%\'
OR data.value LIKE \'%'.db_input($_REQUEST['q'], false).'%\')';
$sql.=' AND ( staff_id='.db_input($thisstaff->getId());
if(($teams=$thisstaff->getTeams()) && count(array_filter($teams)))
$sql.=' OR team_id IN('.implode(',', db_input(array_filter($teams))).')';
if(!$thisstaff->showAssignedOnly() && ($depts=$thisstaff->getDepts()))
$sql.=' OR dept_id IN ('.implode(',', db_input($depts)).')';
$sql.=' ) '
.' GROUP BY email.address '
.' ORDER BY ticket.created LIMIT '.$limit;
if(($res=db_query($sql)) && db_num_rows($res)) {
while(list($email, $count)=db_fetch_row($res))
$tickets[] = array('email'=>$email, 'value'=>$email,
'info'=>"$email ($count)", 'matches'=>$_REQUEST['q']);
}
return $this->json_encode($tickets);
}
function _search($req) {
global $thisstaff, $cfg;
$result=array();
$select = 'SELECT DISTINCT ticket.ticket_id';
$from = ' FROM '.TICKET_TABLE.' ticket ';
$where = ' WHERE 1 ';
//Access control.
$where.=' AND ( ticket.staff_id='.db_input($thisstaff->getId());
if(($teams=$thisstaff->getTeams()) && count(array_filter($teams)))
$where.=' OR ticket.team_id IN('.implode(',', db_input(array_filter($teams))).')';
if(!$thisstaff->showAssignedOnly() && ($depts=$thisstaff->getDepts()))
$where.=' OR ticket.dept_id IN ('.implode(',', db_input($depts)).')';
$where.=' ) ';
//Department
if($req['deptId'])
$where.=' AND ticket.dept_id='.db_input($req['deptId']);
//Help topic
if($req['topicId'])
$where.=' AND ticket.topic_id='.db_input($req['topicId']);
//Status
switch(strtolower($req['status'])) {
case 'open':
$where.=' AND ticket.status="open" ';
break;
case 'answered':
$where.=' AND ticket.status="open" AND ticket.isanswered=1 ';
break;
case 'overdue':
$where.=' AND ticket.status="open" AND ticket.isoverdue=1 ';
break;
case 'closed':
$where.=' AND ticket.status="closed" ';
break;
}
//Assignee
if(isset($req['assignee']) && strcasecmp($req['status'], 'closed')) {
$id=preg_replace("/[^0-9]/", "", $req['assignee']);
$assignee = $req['assignee'];
$where.= ' AND ( ( ticket.status="open" ';
if($assignee[0]=='t')
$where.=' AND ticket.team_id='.db_input($id);
elseif($assignee[0]=='s')
$where.=' AND ticket.staff_id='.db_input($id);
elseif(is_numeric($id))
$where.=' AND ticket.staff_id='.db_input($id);
$where.=')';
if($req['staffId'] && !$req['status']) //Assigned TO + Closed By
$where.= ' OR (ticket.staff_id='.db_input($req['staffId']). ' AND ticket.status="closed") ';
elseif(isset($req['staffId'])) // closed by any
$where.= ' OR ticket.status="closed" ';
$where.= ' ) ';
} elseif($req['staffId']) {
$where.=' AND (ticket.staff_id='.db_input($req['staffId']).' AND ticket.status="closed") ';
}
//dates
$startTime =($req['startDate'] && (strlen($req['startDate'])>=8))?strtotime($req['startDate']):0;
$endTime =($req['endDate'] && (strlen($req['endDate'])>=8))?strtotime($req['endDate']):0;
if( ($startTime && $startTime>time()) or ($startTime>$endTime && $endTime>0))
$startTime=$endTime=0;
if($startTime)
$where.=' AND ticket.created>=FROM_UNIXTIME('.$startTime.')';
if($endTime)
$where.=' AND ticket.created<=FROM_UNIXTIME('.$endTime.')';
//Query
if($req['query']) {
$queryterm=db_real_escape($req['query'], false);
$from.=' LEFT JOIN '.TICKET_THREAD_TABLE.' thread ON (ticket.ticket_id=thread.ticket_id )'
.' LEFT JOIN '.FORM_ENTRY_TABLE.' tentry ON (tentry.object_id = ticket.ticket_id
AND tentry.object_type="T")
LEFT JOIN '.FORM_ANSWER_TABLE.' tans ON (tans.entry_id = tentry.id
AND tans.value_id IS NULL)
LEFT JOIN '.FORM_ENTRY_TABLE.' uentry ON (uentry.object_id = ticket.user_id
AND uentry.object_type="U")
LEFT JOIN '.FORM_ANSWER_TABLE.' uans ON (uans.entry_id = uentry.id
AND uans.value_id IS NULL)
LEFT JOIN '.USER_TABLE.' user ON (ticket.user_id = user.id)
LEFT JOIN '.USER_EMAIL_TABLE.' uemail ON (user.id = uemail.user_id)';
$where.=" AND ( uemail.address LIKE '%$queryterm%'"
." OR user.name LIKE '%$queryterm%'"
." OR tans.value LIKE '%$queryterm%'"
." OR uans.value LIKE '%$queryterm%'"
." OR thread.title LIKE '%$queryterm%'"
." OR thread.body LIKE '%$queryterm%'"
.' )';
}
// Dynamic fields
$dynfields='(SELECT entry.object_id, value, value_id FROM '.FORM_ANSWER_TABLE.' ans '.
'LEFT JOIN '.FORM_ENTRY_TABLE.' entry ON entry.id=ans.entry_id '.
'LEFT JOIN '.FORM_FIELD_TABLE.' field ON field.id=ans.field_id '.
'WHERE field.name = %1$s AND entry.object_type="T")';
foreach (TicketForm::getInstance()->getFields() as $f) {
if ($f->get('name') && isset($req[$f->getFormName()])
&& ($val = $req[$f->getFormName()])) {
$name = 'dyn_'.$f->get('id');
$from .= ' LEFT JOIN '.sprintf($dynfields, db_input($f->get('name')))
." $name ON ($name.object_id = ticket.ticket_id)";
$where .= " AND ($name.value_id = ".db_input($val)
. " OR $name.value LIKE '%".db_real_escape($val)."%')";
}
}
$sql="$select $from $where";
$res = db_query($sql);
while (list($tickets[]) = db_fetch_row($res));
$tickets = array_filter($tickets);
return $tickets;
}
function search() {
$tickets = self::_search($_REQUEST);
if (count($tickets)) {
$uid = md5($_SERVER['QUERY_STRING']);
$_SESSION["adv_$uid"] = $tickets;
$result['success'] =sprintf(
"Search criteria matched %d %s - <a href='tickets.php?%s'>view</a>",
count($tickets), (count($tickets)>1?"tickets":"ticket"),
'advsid='.$uid
);
} else {
$result['fail']='No tickets found matching your search criteria.';
}
return $this->json_encode($result);
}
function acquireLock($tid) {
global $cfg,$thisstaff;
if(!$tid or !is_numeric($tid) or !$thisstaff or !$cfg or !$cfg->getLockTime())
return 0;
if(!($ticket = Ticket::lookup($tid)) || !$ticket->checkStaffAccess($thisstaff))
return $this->json_encode(array('id'=>0, 'retry'=>false, 'msg'=>'Lock denied!'));
//is the ticket already locked?
if($ticket->isLocked() && ($lock=$ticket->getLock()) && !$lock->isExpired()) {
/*Note: Ticket->acquireLock does the same logic...but we need it here since we need to know who owns the lock up front*/
//Ticket is locked by someone else.??
if($lock->getStaffId()!=$thisstaff->getId())
return $this->json_encode(array('id'=>0, 'retry'=>false, 'msg'=>'Unable to acquire lock.'));
//Ticket already locked by staff...try renewing it.
$lock->renew(); //New clock baby!
} elseif(!($lock=$ticket->acquireLock($thisstaff->getId(),$cfg->getLockTime()))) {
//unable to obtain the lock..for some really weired reason!
//Client should watch for possible loop on retries. Max attempts?
return $this->json_encode(array('id'=>0, 'retry'=>true));
}
return $this->json_encode(array('id'=>$lock->getId(), 'time'=>$lock->getTime()));
}
function renewLock($tid, $id) {
global $thisstaff;
if(!$id or !is_numeric($id) or !$thisstaff)
return $this->json_encode(array('id'=>0, 'retry'=>true));
$lock= TicketLock::lookup($id);
if(!$lock || !$lock->getStaffId() || $lock->isExpired()) //Said lock doesn't exist or is is expired
return self::acquireLock($tid); //acquire the lock
if($lock->getStaffId()!=$thisstaff->getId()) //user doesn't own the lock anymore??? sorry...try to next time.
return $this->json_encode(array('id'=>0, 'retry'=>false)); //Give up...
//Renew the lock.
$lock->renew(); //Failure here is not an issue since the lock is not expired yet.. client need to check time!
return $this->json_encode(array('id'=>$lock->getId(), 'time'=>$lock->getTime()));
}
function releaseLock($tid, $id=0) {
global $thisstaff;
if($id && is_numeric($id)){ //Lock Id provided!
$lock = TicketLock::lookup($id, $tid);
//Already gone?
if(!$lock || !$lock->getStaffId() || $lock->isExpired()) //Said lock doesn't exist or is is expired
return 1;
//make sure the user actually owns the lock before releasing it.
return ($lock->getStaffId()==$thisstaff->getId() && $lock->release())?1:0;
}elseif($tid){ //release all the locks the user owns on the ticket.
return TicketLock::removeStaffLocks($thisstaff->getId(),$tid)?1:0;
}
return 0;
}
function previewTicket ($tid) {
global $thisstaff;
if(!$thisstaff || !($ticket=Ticket::lookup($tid)) || !$ticket->checkStaffAccess($thisstaff))
Http::response(404, 'No such ticket');
$staff=$ticket->getStaff();
$lock=$ticket->getLock();
$error=$msg=$warn=null;
if($lock && $lock->getStaffId()==$thisstaff->getId())
$warn.=' <span class="Icon lockedTicket">Ticket is locked by '.$lock->getStaffName().'</span>';
elseif($ticket->isOverdue())
$warn.=' <span class="Icon overdueTicket">Marked overdue!</span>';
ob_start();
echo sprintf(
'<div style="width:500px; padding: 2px 2px 0 5px;">
<h2>%s</h2><br>',Format::htmlchars($ticket->getSubject()));
if($error)
echo sprintf('<div id="msg_error">%s</div>',$error);
elseif($msg)
echo sprintf('<div id="msg_notice">%s</div>',$msg);
elseif($warn)
echo sprintf('<div id="msg_warning">%s</div>',$warn);
echo '<table border="0" cellspacing="" cellpadding="1" width="100%" class="ticket_info">';
$ticket_state=sprintf('<span>%s</span>',ucfirst($ticket->getStatus()));
if($ticket->isOpen()) {
if($ticket->isOverdue())
$ticket_state.=' — <span>Overdue</span>';
else
$ticket_state.=sprintf(' — <span>%s</span>',$ticket->getPriority());
}
echo sprintf('
<tr>
<th width="100">Ticket State:</th>
<td>%s</td>
</tr>
<tr>
<th>Create Date:</th>
<td>%s</td>
</tr>',$ticket_state,
Format::db_datetime($ticket->getCreateDate()));
if($ticket->isClosed()) {
echo sprintf('
<tr>
<th>Close Date:</th>
<td>%s <span class="faded">by %s</span></td>
</tr>',
Format::db_datetime($ticket->getCloseDate()),
($staff?$staff->getName():'staff')
);
} elseif($ticket->getEstDueDate()) {
echo sprintf('
<tr>
<th>Due Date:</th>
<td>%s</td>
</tr>',
Format::db_datetime($ticket->getEstDueDate()));
}
echo '</table>';
echo '<hr>
<table border="0" cellspacing="" cellpadding="1" width="100%" class="ticket_info">';
if($ticket->isOpen()) {
echo sprintf('
<tr>
<th width="100">Assigned To:</th>
<td>%s</td>
</tr>',$ticket->isAssigned()?implode('/', $ticket->getAssignees()):' <span class="faded">— Unassigned —</span>');
}
echo sprintf(
' <tr>
<th width="100">Department:</th>
<td>%s</td>
</tr>
<tr>
<th>Help Topic:</th>
<td>%s</td>
</tr>
<tr>
<th>From:</th>
<td>%s <span class="faded">%s</span></td>
</tr>',
Format::htmlchars($ticket->getDeptName()),
Format::htmlchars($ticket->getHelpTopic()),
Format::htmlchars($ticket->getName()),
$ticket->getEmail());
echo '
</table>';
$options[]=array('action'=>'Thread ('.$ticket->getThreadCount().')','url'=>"tickets.php?id=$tid");
if($ticket->getNumNotes())
$options[]=array('action'=>'Notes ('.$ticket->getNumNotes().')','url'=>"tickets.php?id=$tid#notes");
if($ticket->isOpen())
$options[]=array('action'=>'Reply','url'=>"tickets.php?id=$tid#reply");
if($thisstaff->canAssignTickets())
$options[]=array('action'=>($ticket->isAssigned()?'Reassign':'Assign'),'url'=>"tickets.php?id=$tid#assign");
if($thisstaff->canTransferTickets())
$options[]=array('action'=>'Transfer','url'=>"tickets.php?id=$tid#transfer");
$options[]=array('action'=>'Post Note','url'=>"tickets.php?id=$tid#note");
if($thisstaff->canEditTickets())
$options[]=array('action'=>'Edit Ticket','url'=>"tickets.php?id=$tid&a=edit");
if($options) {
echo '<ul class="tip_menu">';
foreach($options as $option)
echo sprintf('<li><a href="%s">%s</a></li>',$option['url'],$option['action']);
echo '</ul>';
}
echo '</div>';
$resp = ob_get_contents();
ob_end_clean();
return $resp;
}
}
?>