2 /*********************************************************************************
3 * SugarCRM Community Edition is a customer relationship management program developed by
4 * SugarCRM, Inc. Copyright (C) 2004-2013 SugarCRM Inc.
6 * This program is free software; you can redistribute it and/or modify it under
7 * the terms of the GNU Affero General Public License version 3 as published by the
8 * Free Software Foundation with the addition of the following permission added
9 * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
10 * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
11 * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
13 * This program is distributed in the hope that it will be useful, but WITHOUT
14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15 * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
18 * You should have received a copy of the GNU Affero General Public License along with
19 * this program; if not, see http://www.gnu.org/licenses or write to the Free
20 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
24 * SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
26 * The interactive user interfaces in modified source and object code versions
27 * of this program must display Appropriate Legal Notices, as required under
28 * Section 5 of the GNU Affero General Public License version 3.
30 * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
31 * these Appropriate Legal Notices must retain the display of the "Powered by
32 * SugarCRM" logo. If the display of the logo is not reasonably feasible for
33 * technical reasons, the Appropriate Legal Notices must display the words
34 * "Powered by SugarCRM".
35 ********************************************************************************/
38 require_once('include/SugarObjects/templates/person/Person.php');
39 require_once('include/MVC/SugarModule.php');
42 * quicksearchQuery class, handles AJAX calls from quicksearch.js
44 * @copyright 2004-2007 SugarCRM Inc.
45 * @license http://www.sugarcrm.com/crm/products/sugar-professional-eula.html SugarCRM Professional End User License
46 * @since Class available since Release 4.5.1
48 class quicksearchQuery
54 const CONDITION_CONTAINS = 'contains';
55 const CONDITION_LIKE_CUSTOM = 'like_custom';
56 const CONDITION_EQUAL = 'equal';
58 protected $extra_where;
61 * Query a module for a list of items
64 * example for querying Account module with 'a':
65 * array ('modules' => array('Accounts'), // module to use
66 * 'field_list' => array('name', 'id'), // fields to select
67 * 'group' => 'or', // how the conditions should be combined
68 * 'conditions' => array(array( // array of where conditions to use
69 * 'name' => 'name', // field
70 * 'op' => 'like_custom', // operation
71 * 'end' => '%', // end of the query
72 * 'value' => 'a', // query value
75 * 'order' => 'name', // order by
76 * 'limit' => '30', // limit, number of records to return
78 * @return array list of elements returned
80 public function query($args)
82 $args = $this->prepareArguments($args);
83 $args = $this->updateQueryArguments($args);
84 $data = $this->getRawResults($args);
86 return $this->getFormattedJsonResults($data, $args);
93 public function get_contact_array($args)
95 $args = $this->prepareArguments($args);
96 $args = $this->updateContactArrayArguments($args);
97 $data = $this->getRawResults($args);
98 $results = $this->prepareResults($data, $args);
100 return $this->getFilteredJsonResults($results);
104 * Returns the list of users, faster than using query method for Users module
106 * @param array $args arguments used to construct query, see query() for example
107 * @return array list of users returned
109 public function get_user_array($args)
111 $condition = $args['conditions'][0]['value'];
112 $results = $this->getUserResults($condition);
114 return $this->getJsonEncodedData($results);
119 * Returns search results from external API
124 public function externalApi($args)
126 require_once('include/externalAPI/ExternalAPIFactory.php');
129 $api = ExternalAPIFactory::loadAPI($args['api']);
130 $data['fields'] = $api->searchDoc($_REQUEST['query']);
131 $data['totalCount'] = count($data['fields']);
132 } catch(Exception $ex) {
133 $GLOBALS['log']->error($ex->getMessage());
136 return $this->getJsonEncodedData($data);
141 * Internal function to construct where clauses
143 * @param Object $focus
147 protected function constructWhere($focus, $args)
149 global $db, $locale, $current_user;
151 $table = $focus->getTableName();
152 if (!empty($table)) {
153 $table_prefix = $db->getValidDBName($table).".";
157 $conditionArray = array();
159 if (!isset($args['conditions']) || !is_array($args['conditions'])) {
160 $args['conditions'] = array();
163 foreach($args['conditions'] as $condition)
165 if (isset($condition['op'])) {
166 $operator = $condition['op'];
173 case self::CONDITION_CONTAINS:
178 $table_prefix . $db->getValidDBName($condition['name']),
179 $db->quote($condition['value']
183 case self::CONDITION_LIKE_CUSTOM:
185 if (!empty($condition['begin'])) {
186 $like .= $db->quote($condition['begin']);
188 $like .= $db->quote($condition['value']);
190 if (!empty($condition['end'])) {
191 $like .= $db->quote($condition['end']);
194 if ($focus instanceof Person){
195 $nameFormat = $locale->getLocaleFormatMacro($current_user);
197 if (strpos($nameFormat,'l') > strpos($nameFormat,'f')) {
200 $db->concat($table, array('first_name','last_name')) . " like '$like'"
205 $db->concat($table, array('last_name','first_name')) . " like '$like'"
212 $table_prefix . $db->getValidDBName($condition['name']) . sprintf(" like '%s'", $like)
217 case self::CONDITION_EQUAL:
218 if ($condition['value']) {
221 sprintf("(%s = '%s')", $db->getValidDBName($condition['name']), $db->quote($condition['value']))
229 $table_prefix.$db->getValidDBName($condition['name']) . sprintf(" like '%s%%'", $db->quote($condition['value']))
234 $whereClauseArray = array();
235 if (!empty($conditionArray)) {
236 $whereClauseArray[] = sprintf('(%s)', implode(" {$args['group']} ", $conditionArray));
238 if(!empty($this->extra_where)) {
239 $whereClauseArray[] = "({$this->extra_where})";
242 if ($table == 'users') {
243 $whereClauseArray[] = "users.status='Active'";
246 return implode(' AND ', $whereClauseArray);
250 * Returns formatted data
252 * @param array $results
256 protected function formatResults($results, $args)
258 global $sugar_config;
260 $app_list_strings = null;
261 $data['totalCount'] = count($results);
262 $data['fields'] = array();
264 for ($i = 0; $i < count($results); $i++) {
265 $data['fields'][$i] = array();
266 $data['fields'][$i]['module'] = $results[$i]->object_name;
268 //C.L.: Bug 43395 - For Quicksearch, do not return values with salutation and title formatting
269 if($results[$i] instanceof Person)
271 $results[$i]->createLocaleFormattedName = false;
273 $listData = $results[$i]->get_list_view_data();
275 foreach ($args['field_list'] as $field) {
277 if ((isset($results[$i]->field_name_map[$field]['type']) && $results[$i]->field_name_map[$field]['type'] == 'enum')
278 || (isset($results[$i]->field_name_map[$field]['custom_type']) && $results[$i]->field_name_map[$field]['custom_type'] == 'enum')) {
280 // get fields to match enum vals
281 if(empty($app_list_strings)) {
282 if(isset($_SESSION['authenticated_user_language']) && $_SESSION['authenticated_user_language'] != '') $current_language = $_SESSION['authenticated_user_language'];
283 else $current_language = $sugar_config['default_language'];
284 $app_list_strings = return_app_list_strings_language($current_language);
287 // match enum vals to text vals in language pack for return
288 if(!empty($app_list_strings[$results[$i]->field_name_map[$field]['options']])) {
289 $results[$i]->$field = $app_list_strings[$results[$i]->field_name_map[$field]['options']][$results[$i]->$field];
294 if (isset($listData[$field])) {
295 $data['fields'][$i][$field] = $listData[$field];
296 } else if (isset($results[$i]->$field)) {
297 $data['fields'][$i][$field] = $results[$i]->$field;
299 $data['fields'][$i][$field] = '';
304 if (is_array($data['fields'])) {
305 foreach ($data['fields'] as $i => $recordIn) {
306 if (!is_array($recordIn)) {
310 foreach ($recordIn as $col => $dataIn) {
311 if (!is_scalar($dataIn)) {
315 $data['fields'][$i][$col] = html_entity_decode($dataIn, ENT_QUOTES, 'UTF-8');
324 * Filter duplicate results from the list
329 protected function filterResults($list)
331 $fieldsFiltered = array();
332 foreach ($list['fields'] as $field) {
334 foreach ($fieldsFiltered as $item) {
335 if ($item === $field) {
342 $fieldsFiltered[] = $field;
346 $list['totalCount'] = count($fieldsFiltered);
347 $list['fields'] = $fieldsFiltered;
353 * Returns raw search results. Filters should be applied later.
356 * @param boolean $singleSelect
359 protected function getRawResults($args, $singleSelect = false)
361 $orderBy = !empty($args['order']) ? $args['order'] : '';
362 $limit = !empty($args['limit']) ? intval($args['limit']) : '';
365 foreach ($args['modules'] as $module) {
366 $focus = SugarModule::get($module)->loadBean();
368 $orderBy = $focus->db->getValidDBName(($args['order_by_name'] && $focus instanceof Person && $args['order'] == 'name') ? 'last_name' : $orderBy);
370 if ($focus->ACLAccess('ListView', true)) {
371 $where = $this->constructWhere($focus, $args);
372 $data = $this->updateData($data, $focus, $orderBy, $where, $limit, $singleSelect);
381 * Returns search results with all fixes applied
387 protected function prepareResults($data, $args)
389 $results['totalCount'] = $count = count($data);
390 $results['fields'] = array();
392 for ($i = 0; $i < $count; $i++) {
394 $field['module'] = $data[$i]->object_name;
396 $field = $this->overrideContactId($field, $data[$i], $args);
397 $field = $this->updateContactName($field, $args);
399 $results['fields'][$i] = $this->prepareField($field, $args);
406 * Returns user search results
408 * @param string $condition
411 protected function getUserResults($condition)
413 $users = $this->getUserArray($condition);
416 $results['totalCount'] = count($users);
417 $results['fields'] = array();
419 foreach ($users as $id => $name) {
423 'id' => (string) $id,
424 'user_name' => $name,
433 * Merges current module search results to given list and returns it
436 * @param SugarBean $focus
437 * @param string $orderBy
438 * @param string $where
439 * @param string $limit
440 * @param boolean $singleSelect
443 protected function updateData($data, $focus, $orderBy, $where, $limit, $singleSelect = false)
445 $result = $focus->get_list($orderBy, $where, 0, $limit, -1, 0, $singleSelect);
447 return array_merge($data, $result['list']);
451 * Updates search result with proper contact name
453 * @param array $result
457 protected function updateContactName($result, $args)
461 $result[$args['field_list'][0]] = $locale->getLocaleFormattedName(
462 $result['first_name'],
463 $result['last_name'],
464 $result['salutation']
471 * Overrides contact_id and reports_to_id params (to 'id')
473 * @param array $result
474 * @param object $data
478 protected function overrideContactId($result, $data, $args)
480 foreach ($args['field_list'] as $field) {
481 $result[$field] = (preg_match('/reports_to_id$/s',$field)
482 || preg_match('/contact_id$/s',$field))
483 ? $data->id // "reports_to_id" to "id"
491 * Returns prepared arguments. Should be redefined in child classes.
493 * @param array $arguments
496 protected function prepareArguments($args)
498 global $sugar_config;
500 // override query limits
501 if (isset($args['limit']) && $sugar_config['list_max_entries_per_page'] < ($args['limit'] + 1)) {
502 $sugar_config['list_max_entries_per_page'] = ($args['limit'] + 1);
506 'order_by_name' => false,
508 $this->extra_where = '';
511 /* BUG: 52684 properly check for 'and' jeff@neposystems.com */
512 if(!empty($args['group']) && strcasecmp($args['group'], 'and') == 0) {
513 $args['group'] = 'AND';
515 $args['group'] = 'OR';
518 return array_merge($defaults, $args);
522 * Returns prepared field array. Should be redefined in child classes.
524 * @param array $field
528 protected function prepareField($field, $args)
536 * @param string $condition
539 protected function getUserArray($condition)
541 return (showFullName())
542 // utils.php, if system is configured to show full name
543 ? getUserArrayFromFullName($condition, true)
544 : get_user_array(false, 'Active', '', false, $condition,' AND portal_only=0 ',false);
548 * Returns additional where condition for non private teams and removes arguments that have been replaced with
549 * custom where clauses
554 protected function getNonPrivateTeamsWhere(&$args)
559 $teams_filtered = false;
561 if (isset($args['conditions']) && is_array($args['conditions'])) {
562 foreach ($args['conditions'] as $i => $condition) {
563 if (isset($condition['name'], $condition['value'])) {
564 switch($condition['name']) {
567 "(teams.name like '%s%%' OR teams.name_2 like '%s%%')",
568 $db->quote($condition['value']),
569 $db->quote($condition['value'])
571 unset($args['conditions'][$i]);
575 "teams.id IN (SELECT team_id FROM team_memberships WHERE user_id = '%s' AND deleted = 0)",
576 $db->quote($condition['value'])
578 unset($args['conditions'][$i]);
579 $teams_filtered = true;
585 if (!$teams_filtered) {
586 $where[] ='teams.private = 0';
589 return implode(' AND ', $where);
593 * Returns JSON encoded data
598 protected function getJsonEncodedData($data)
600 $json = getJSONobj();
602 return $json->encodeReal($data);
606 * Returns formatted JSON encoded search results
609 * @param array $results
612 protected function getFormattedJsonResults($results, $args)
614 $results = $this->formatResults($results, $args);
616 return $this->getJsonEncodedData($results);
620 * Returns filtered JSON encoded search results
622 * @param array $results
625 protected function getFilteredJsonResults($results)
627 $results = $this->filterResults($results);
629 return $this->getJsonEncodedData($results);
633 * Returns updated arguments array
638 protected function updateQueryArguments($args)
640 $args['order_by_name'] = true;
646 * Returns updated arguments array for contact query
651 protected function updateContactArrayArguments($args)
657 * Returns updated arguments array for team query
662 protected function updateTeamArrayArguments($args)
664 $this->extra_where = $this->getNonPrivateTeamsWhere($args);
665 $args['modules'] = array('Teams');