diff --git a/include/ajax.tickets.php b/include/ajax.tickets.php index 708bb6dbdc88b3f6adadba7205cc60efb5da8b4c..d5d12bea93f519e3b443038fbcdda61f35615566 100644 --- a/include/ajax.tickets.php +++ b/include/ajax.tickets.php @@ -97,184 +97,6 @@ class TicketsAjaxAPI extends AjaxController { return $this->json_encode($tickets); } - function _search($req) { - global $thisstaff, $cfg, $ost; - - $result=array(); - $criteria = array(); - - $select = 'SELECT ticket.ticket_id'; - $from = ' FROM '.TICKET_TABLE.' ticket - LEFT JOIN '.TICKET_STATUS_TABLE.' status - ON (status.id = ticket.status_id) '; - //Access control. - $where = ' WHERE ( (ticket.staff_id='.db_input($thisstaff->getId()) - .' AND status.state="open" )'; - - if(($teams=$thisstaff->getTeams()) && count(array_filter($teams))) - $where.=' OR (ticket.team_id IN ('.implode(',', db_input(array_filter($teams))) - .' ) AND status.state="open" )'; - - 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']); - $criteria['dept_id'] = $req['deptId']; - } - - //Help topic - if($req['topicId']) { - $where.=' AND ticket.topic_id='.db_input($req['topicId']); - $criteria['topic_id'] = $req['topicId']; - } - - // Status - if ($req['statusId'] - && ($status=TicketStatus::lookup($req['statusId']))) { - $where .= sprintf(' AND status.id="%d" ', - $status->getId()); - $criteria['status_id'] = $status->getId(); - } - - // Flags - if ($req['flag']) { - switch (strtolower($req['flag'])) { - case 'answered': - $where .= ' AND ticket.isanswered =1 '; - $criteria['isanswered'] = 1; - $criteria['state'] = 'open'; - $where .= ' AND status.state="open" '; - break; - case 'overdue': - $where .= ' AND ticket.isoverdue =1 '; - $criteria['isoverdue'] = 1; - $criteria['state'] = 'open'; - $where .= ' AND status.state="open" '; - break; - } - } - - //Assignee - if($req['assignee'] && strcasecmp($req['status'], 'closed')) { # assigned-to - $id=preg_replace("/[^0-9]/", "", $req['assignee']); - $assignee = $req['assignee']; - $where.= ' AND ( ( status.state="open" '; - if($assignee[0]=='t') { - $where.=' AND ticket.team_id='.db_input($id); - $criteria['team_id'] = $id; - } - elseif($assignee[0]=='s' || is_numeric($id)) { - $where.=' AND ticket.staff_id='.db_input($id); - $criteria['staff_id'] = $id; - } - - $where.=')'; - - if($req['staffId'] && !$req['status']) //Assigned TO + Closed By - $where.= ' OR (ticket.staff_id='.db_input($req['staffId']). - ' AND status.state IN("closed")) '; - elseif($req['staffId']) // closed by any - $where.= ' OR status.state IN("closed") '; - - $where.= ' ) '; - } elseif($req['staffId']) { # closed-by - $where.=' AND (ticket.staff_id='.db_input($req['staffId']).' AND - status.state IN("closed")) '; - $criteria['state__in'] = array('closed'); - $criteria['staff_id'] = $req['staffId']; - } - - //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.')'; - $criteria['created__gte'] = $startTime; - } - - if($endTime) { - $where.=' AND ticket.created<=FROM_UNIXTIME('.$endTime.')'; - $criteria['created__lte'] = $startTime; - } - - // Dynamic fields - $cdata_search = false; - foreach (TicketForm::getInstance()->getFields() as $f) { - if (isset($req[$f->getFormName()]) - && ($val = $req[$f->getFormName()])) { - $name = $f->get('name') ? $f->get('name') - : 'field_'.$f->get('id'); - if (is_array($val)) { - $cwhere = '(' . implode(' OR ', array_map( - function($k) use ($name) { - return sprintf('FIND_IN_SET(%s, `%s`)', db_input($k), $name); - }, $val) - ) . ')'; - $criteria["cdata.{$name}"] = $val; - } - else { - $cwhere = "cdata.`$name` LIKE '%".db_real_escape($val)."%'"; - $criteria["cdata.{$name}"] = $val; - } - $where .= ' AND ('.$cwhere.')'; - $cdata_search = true; - } - } - if ($cdata_search) - $from .= 'LEFT JOIN '.TABLE_PREFIX.'ticket__cdata ' - ." cdata ON (cdata.ticket_id = ticket.ticket_id)"; - - //Query - $joins = array(); - if($req['query']) { - // Setup sets of joins and queries - if ($s = $ost->searcher) - return $s->find($req['query'], $criteria, 'Ticket'); - } - - $sections = array(); - foreach ($joins as $j) { - $sections[] = "$select $from {$j['from']} $where AND ({$j['where']})"; - } - if (!$joins) - $sections[] = "$select $from $where"; - - $sql=implode(' union ', $sections); - if (!($res = db_query($sql))) - return TicketForm::dropDynamicDataView(); - - $tickets = array(); - while ($row = db_fetch_row($res)) - $tickets[] = $row[0]; - - return $tickets; - } - - function search() { - $tickets = self::_search($_REQUEST); - $result = array(); - - if (count($tickets)) { - $uid = md5($_SERVER['QUERY_STRING']); - $_SESSION["adv_$uid"] = $tickets; - $result['success'] = sprintf(__("Search criteria matched %s"), - sprintf(_N('%d ticket', '%d tickets', count($tickets)), count($tickets) - )) - . " - <a href='tickets.php?advsid=$uid'>".__('view')."</a>"; - } else { - $result['fail']=__('No tickets found matching your search criteria.'); - } - - return $this->json_encode($result); - } - function acquireLock($tid) { global $cfg,$thisstaff; diff --git a/include/class.search.php b/include/class.search.php index e7647cc3711330adfd9fa5566b895c686b231aa3..3a75ad09d950fcfefc36b53b49af9de5c2f989dc 100644 --- a/include/class.search.php +++ b/include/class.search.php @@ -31,7 +31,7 @@ abstract class SearchBackend { const SORT_OLDEST = 3; abstract function update($model, $id, $content, $new=false, $attrs=array()); - abstract function find($query, $criteria, $model=false, $sort=array()); + abstract function find($query, QuerySet $criteria); static function register($backend=false) { $backend = $backend ?: get_called_class(); @@ -61,9 +61,9 @@ class SearchInterface { $this->bootstrap(); } - function find($query, $criteria, $model=false, $sort=array()) { + function find($query, QuerySet $criteria) { $query = Format::searchable($query); - return $this->backend->find($query, $criteria, $model, $sort); + return $this->backend->find($query, $criteria); } function update($model, $id, $content, $new=false, $attrs=array()) { @@ -278,7 +278,7 @@ class MysqlSearchBackend extends SearchBackend { return implode(' ', $results); } - function find($query, $criteria=array(), $model=false, $sort=array()) { + function find($query, QuerySet $criteria) { global $thisstaff; $mode = ' IN BOOLEAN MODE'; @@ -292,63 +292,32 @@ class MysqlSearchBackend extends SearchBackend { $P = TABLE_PREFIX; $sort = ''; - if ($query) { - $tables[] = "( - SELECT object_type, object_id, $search AS `relevance` - FROM `{$P}_search` `search` - WHERE $search - ) `search`"; - $sort = 'ORDER BY `search`.`relevance`'; - } - - switch ($model) { + switch ($criteria->model) { case false: - case 'Ticket': - $tables[] = "(select ticket_id as ticket_id from {$P}ticket - ) B1 ON (B1.ticket_id = search.object_id and search.object_type = 'T')"; - $tables[] = "(select A2.id as thread_id, A1.ticket_id from {$P}ticket A1 - join {$P}ticket_thread A2 on (A1.ticket_id = A2.ticket_id) - ) B2 ON (B2.thread_id = search.object_id and search.object_type = 'H')"; - $tables[] = "(select A3.id as user_id, A1.ticket_id from {$P}user A3 - join {$P}ticket A1 on (A1.user_id = A3.id) - ) B3 ON (B3.user_id = search.object_id and search.object_type = 'U')"; - $tables[] = "(select A4.id as org_id, A1.ticket_id from {$P}organization A4 - join {$P}user A3 on (A3.org_id = A4.id) join {$P}ticket A1 on (A1.user_id = A3.id) - ) B4 ON (B4.org_id = search.object_id and search.object_type = 'O')"; - $key = 'COALESCE(B1.ticket_id, B2.ticket_id, B3.ticket_id, B4.ticket_id)'; - $tables[] = "{$P}ticket A1 ON (A1.ticket_id = {$key})"; - $tables[] = "{$P}ticket_status A2 ON (A1.status_id = A2.id)"; - $cdata_search = false; - $where = array(); - - if ($criteria) { - foreach ($criteria as $name=>$value) { - switch ($name) { - case 'status_id': - $where[] = 'A2.id = '.db_input($value); - break; - case 'state': - $where[] = 'A2.state = '.db_input($value); - break; - case 'state__in': - $where[] = 'A2.state IN ('.implode(',',db_input($value)).')'; - break; - case 'topic_id': - case 'staff_id': - case 'team_id': - case 'dept_id': - case 'user_id': - case 'isanswered': - case 'isoverdue': - case 'number': - $where[] = sprintf('A1.%s = %s', $name, db_input($value)); - break; - case 'created__gte': - $where[] = sprintf('A1.created >= %s', db_input($value)); - break; - case 'created__lte': - $where[] = sprintf('A1.created <= %s', db_input($value)); - break; + case 'TicketModel': + if ($query) { + $key = 'COALESCE(Z1.ticket_id, Z2.ticket_id)'; + $criteria->extra(array( + 'select' => array( + 'key' => $key, + 'relevance'=>'`search`.`relevance`', + ), + 'order_by' => array('relevance'), + 'tables' => array( + "(SELECT object_type, object_id, $search AS `relevance` + FROM `{$P}_search` `search` WHERE $search) `search`", + "(select ticket_id as ticket_id from {$P}ticket + ) Z1 ON (Z1.ticket_id = search.object_id and search.object_type = 'T')", + "(select A2.id as thread_id, A1.ticket_id from {$P}ticket A1 + join {$P}ticket_thread A2 on (A1.ticket_id = A2.ticket_id) + ) Z2 ON (Z2.thread_id = search.object_id and search.object_type = 'H')", + ) + )); + // XXX: This is extremely ugly + $criteria->filter(array('ticket_id'=>new SqlCode($key))); + $criteria->distinct('ticket_id'); + } + /* case 'email': case 'org_id': case 'form_id': @@ -371,57 +340,17 @@ class MysqlSearchBackend extends SearchBackend { } } } - } - if ($cdata_search) - $tables[] = TABLE_PREFIX.'ticket__cdata cdata' - .' ON (cdata.ticket_id = A1.ticket_id)'; - - // Always consider the current staff's access - $thisstaff->getDepts(); - $access = array(); - $access[] = '(A1.staff_id=' . db_input($thisstaff->getId()) - .' AND A2.state="open")'; - - if (!$thisstaff->showAssignedOnly() && ($depts=$thisstaff->getDepts())) - $access[] = 'A1.dept_id IN (' - . ($depts ? implode(',', db_input($depts)) : 0) - . ')'; - - if (($teams = $thisstaff->getTeams()) && count(array_filter($teams))) - $access[] = 'A1.team_id IN (' - .implode(',', db_input(array_filter($teams))) - .') AND A2.state="open"'; - - $where[] = '(' . implode(' OR ', $access) . ')'; + */ // TODO: Consider sorting preferences - - $sql = 'SELECT DISTINCT ' - . $key - . ' FROM ' - . implode(' LEFT JOIN ', $tables) - . ' WHERE ' . implode(' AND ', $where) - . $sort - . ' LIMIT 500'; } - $class = get_class(); - $auto_create = function($db_error) use ($class) { - - if ($db_error != 1146) - // Perform the standard error handling - return true; - + // TODO: Ensure search table exists; + if (false) { // Create the search table automatically $class::createSearchTable(); - }; - $res = db_query($sql, $auto_create); - $object_ids = array(); - - while ($row = db_fetch_row($res)) - $object_ids[] = $row[0]; - - return $object_ids; + } + return $criteria; } static function createSearchTable() { @@ -687,6 +616,9 @@ class SavedSearch extends VerySimpleModel { foreach ($form->getFields() as $F) { if (substr($F->get('name'), -7) == '+search' && $F->getClean()) $selected += 1; + // Consider keyword searches + elseif ($F->get('name') == 'keywords' && $F->getClean()) + $selected += 1; } if (!$selected) $form->addError('No fields selected for searching'); @@ -778,6 +710,14 @@ class SavedSearch extends VerySimpleModel { } } } + + // Consider keyword searching + if ($keywords = $form->getField('keywords')->getClean()) { + global $ost; + + $qs = $ost->searcher->find($keywords, $qs); + } + return $qs; }