]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - modules/Home/QuickSearch.php
Release 6.4.3
[Github/sugarcrm.git] / modules / Home / QuickSearch.php
1 <?php
2 /*********************************************************************************
3  * SugarCRM Community Edition is a customer relationship management program developed by
4  * SugarCRM, Inc. Copyright (C) 2004-2012 SugarCRM Inc.
5  * 
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.
12  * 
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
16  * details.
17  * 
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
21  * 02110-1301 USA.
22  * 
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.
25  * 
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.
29  * 
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  ********************************************************************************/
36
37
38 require_once('include/SugarObjects/templates/person/Person.php');
39 require_once('include/MVC/SugarModule.php');
40
41 /**
42  * quicksearchQuery class, handles AJAX calls from quicksearch.js
43  *
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
47  */
48 class quicksearchQuery
49 {
50     /**
51      * Condition operators
52      * @var string
53      */
54     const CONDITION_CONTAINS    = 'contains';
55     const CONDITION_LIKE_CUSTOM = 'like_custom';
56     const CONDITION_EQUAL       = 'equal';
57
58     /**
59      * Query a module for a list of items
60      *
61      * @param array $args
62      * example for querying Account module with 'a':
63      * array ('modules' => array('Accounts'), // module to use
64      *        'field_list' => array('name', 'id'), // fields to select
65      *        'group' => 'or', // how the conditions should be combined
66      *        'conditions' => array(array( // array of where conditions to use
67      *                              'name' => 'name', // field
68      *                              'op' => 'like_custom', // operation
69      *                              'end' => '%', // end of the query
70      *                              'value' => 'a',  // query value
71      *                              )
72      *                        ),
73      *        'order' => 'name', // order by
74      *        'limit' => '30', // limit, number of records to return
75      *       )
76      * @return array list of elements returned
77      */
78     public function query($args)
79     {
80         $args = $this->prepareArguments($args);
81         $args = $this->updateQueryArguments($args);
82         $data = $this->getRawResults($args);
83
84         return $this->getFormattedJsonResults($data, $args);
85     }
86
87     /**
88      * get_contact_array
89      *
90      */
91     public function get_contact_array($args)
92     {
93         $args    = $this->prepareArguments($args);
94         $args    = $this->updateContactArrayArguments($args);
95         $data    = $this->getRawResults($args);
96         $results = $this->prepareResults($data, $args);
97
98         return $this->getFilteredJsonResults($results);
99     }
100
101     /**
102      * Returns the list of users, faster than using query method for Users module
103      *
104      * @param array $args arguments used to construct query, see query() for example
105      * @return array list of users returned
106      */
107     public function get_user_array($args)
108     {
109         $condition = $args['conditions'][0]['value'];
110         $results   = $this->getUserResults($condition);
111
112         return $this->getJsonEncodedData($results);
113     }
114
115
116     /**
117      * Returns search results from external API
118      *
119      * @param array $args
120      * @return array
121      */
122     public function externalApi($args)
123     {
124         require_once('include/externalAPI/ExternalAPIFactory.php');
125         $data = array();
126         try {
127             $api = ExternalAPIFactory::loadAPI($args['api']);
128             $data['fields']     = $api->searchDoc($_REQUEST['query']);
129             $data['totalCount'] = count($data['fields']);
130         } catch(Exception $ex) {
131             $GLOBALS['log']->error($ex->getMessage());
132         }
133
134         return $this->getJsonEncodedData($data);
135     }
136
137     /**
138      * Internal function to construct where clauses
139      *
140      * @param Object $focus
141      * @param array $args
142      * @return string
143      */
144     protected function constructWhere($focus, $args)
145     {
146         global $db, $locale, $current_user;
147
148         $table = $focus->getTableName();
149         if (!empty($table)) {
150             $table .= ".";
151         }
152         $conditionArray = array();
153
154         if (!is_array($args['conditions'])) {
155             $args['conditions'] = array();
156         }
157
158         foreach($args['conditions'] as $condition)
159         {
160             switch ($condition['op'])
161             {
162                 case self::CONDITION_CONTAINS:
163                     array_push(
164                         $conditionArray,
165                         sprintf(
166                             "%s like '%%%s%%'",
167                             $db->quote($table . $condition['name']),
168                             $db->quote($condition['value']
169                     )));
170                     break;
171
172                 case self::CONDITION_LIKE_CUSTOM:
173                     $like = '';
174                     if (!empty($condition['begin'])) {
175                         $like .= $db->quote($condition['begin']);
176                     }
177                     $like .= $db->quote($condition['value']);
178
179                     if (!empty($condition['end'])) {
180                         $like .= $db->quote($condition['end']);
181                     }
182
183                     if ($focus instanceof Person){
184                         $nameFormat = $locale->getLocaleFormatMacro($current_user);
185
186                         if (strpos($nameFormat,'l') > strpos($nameFormat,'f')) {
187                             array_push(
188                                 $conditionArray,
189                                 db_concat(rtrim($table, '.'), array('first_name','last_name')) . sprintf(" like '%s'", $like)
190                             );
191                         } else {
192                             array_push(
193                                 $conditionArray,
194                                 db_concat(rtrim($table, '.'), array('last_name','first_name')) . sprintf(" like '%s'", $like)
195                             );
196                         }
197                     }
198                     else {
199                         array_push(
200                             $conditionArray,
201                             $db->quote($table . $condition['name']) . sprintf(" like '%s'", $like)
202                         );
203                     }
204                     break;
205
206                 case self::CONDITION_EQUAL:
207                     if ($condition['value']) {
208                         if (empty($args['whereExtra'])) {
209                             $args['whereExtra'] = '';
210                         }
211                         $args['whereExtra'] .= sprintf("(%s = '%s')", $db->quote($condition['name']), $db->quote($condition['value']));
212                     }
213                     break;
214
215                 default:
216                     array_push(
217                         $conditionArray,
218                         $db->quote($table.$condition['name']) . sprintf(" like '%s%%", $db->quote($condition['value']))
219                     );
220             }
221         }
222
223         $whereClause = sprintf('(%s)', implode(" {$args['group']} ", $conditionArray));
224
225         if ($table == 'users.') {
226             $whereClause .= sprintf(" AND %sstatus='Active'", $table);
227         }
228
229         // Need to include the default whereStatement
230         if (!empty($args['whereExtra'])) {
231             if (!empty($whereClause)) {
232                 $whereClause .= ' AND ';
233             }
234             $whereClause .= html_entity_decode($args['whereExtra'], ENT_QUOTES);
235         }
236
237         return $whereClause;
238     }
239
240     /**
241      * Returns formatted data
242      *
243      * @param array $results
244      * @param array $args
245      * @return array
246      */
247     protected function formatResults($results, $args)
248     {
249         global $sugar_config;
250
251         $app_list_strings = null;
252         $data['totalCount'] = count($results);
253         $data['fields']     = array();
254
255         for ($i = 0; $i < count($results); $i++) {
256             $data['fields'][$i] = array();
257             $data['fields'][$i]['module'] = $results[$i]->object_name;
258
259             //C.L.: Bug 43395 - For Quicksearch, do not return values with salutation and title formatting
260             if($results[$i] instanceof Person)
261             {
262                 $results[$i]->createLocaleFormattedName = false;
263             }
264             $listData = $results[$i]->get_list_view_data();
265
266             foreach ($args['field_list'] as $field) {
267                 // handle enums
268                 if ((isset($results[$i]->field_name_map[$field]['type']) && $results[$i]->field_name_map[$field]['type'] == 'enum')
269                     || (isset($results[$i]->field_name_map[$field]['custom_type']) && $results[$i]->field_name_map[$field]['custom_type'] == 'enum')) {
270
271                     // get fields to match enum vals
272                     if(empty($app_list_strings)) {
273                         if(isset($_SESSION['authenticated_user_language']) && $_SESSION['authenticated_user_language'] != '') $current_language = $_SESSION['authenticated_user_language'];
274                         else $current_language = $sugar_config['default_language'];
275                         $app_list_strings = return_app_list_strings_language($current_language);
276                     }
277
278                     // match enum vals to text vals in language pack for return
279                     if(!empty($app_list_strings[$results[$i]->field_name_map[$field]['options']])) {
280                         $results[$i]->$field = $app_list_strings[$results[$i]->field_name_map[$field]['options']][$results[$i]->$field];
281                     }
282                 }
283
284
285                 if (isset($listData[$field])) {
286                     $data['fields'][$i][$field] = $listData[$field];
287                 } else if (isset($results[$i]->$field)) {
288                     $data['fields'][$i][$field] = $results[$i]->$field;
289                 } else {
290                     $data['fields'][$i][$field] = '';
291                 }
292             }
293         }
294
295         if (is_array($data['fields'])) {
296             foreach ($data['fields'] as $i => $recordIn) {
297                 if (!is_array($recordIn)) {
298                     continue;
299                 }
300
301                 foreach ($recordIn as $col => $dataIn) {
302                     if (!is_scalar($dataIn)) {
303                         continue;
304                     }
305
306                     $data['fields'][$i][$col] = html_entity_decode($dataIn, ENT_QUOTES, 'UTF-8');
307                 }
308             }
309         }
310
311         return $data;
312     }
313
314     /**
315      * Filter duplicate results from the list
316      *
317      * @param array $list
318      * @return  array
319      */
320     protected function filterResults($list)
321     {
322         $fieldsFiltered = array();
323         foreach ($list['fields'] as $field) {
324             $found = false;
325             foreach ($fieldsFiltered as $item) {
326                 if ($item === $field) {
327                     $found = true;
328                     break;
329                 }
330             }
331
332             if (!$found) {
333                 $fieldsFiltered[] = $field;
334             }
335         }
336
337         $list['totalCount'] = count($fieldsFiltered);
338         $list['fields']     = $fieldsFiltered;
339
340         return $list;
341     }
342
343     /**
344      * Returns raw search results. Filters should be applied later.
345      *
346      * @param array $args
347      * @param boolean $singleSelect
348      * @return array
349      */
350     protected function getRawResults($args, $singleSelect = false)
351     {
352         $orderBy = !empty($args['order']) ? $args['order'] : '';
353         $limit   = !empty($args['limit']) ? $args['limit'] : '';
354         $data    = array();
355
356         foreach ($args['modules'] as $module) {
357             $focus = SugarModule::get($module)->loadBean();
358
359             $orderBy = ($args['order_by_name'] && $focus instanceof Person && $args['order'] == 'name') ? 'last_name' : $orderBy;
360
361             if ($focus->ACLAccess('ListView', true)) {
362                 $where = ($args['use_default_where']) ? $this->constructWhere($focus, $args) : $args['whereExtra'];
363                 $data  = $this->updateData($data, $focus, $orderBy, $where, $limit, $singleSelect);
364             }
365         }
366
367         return $data;
368     }
369
370     /**
371      * Returns search results with all fixes applied
372      *
373      * @param array $data
374      * @param array $args
375      * @return array
376      */
377     protected function prepareResults($data, $args)
378     {
379         $results['totalCount'] = $count = count($data);
380         $results['fields']     = array();
381
382         for ($i = 0; $i < $count; $i++) {
383             $field = array();
384             $field['module'] = $data[$i]->object_name;
385
386             $field = $this->overrideContactId($field, $data[$i], $args);
387             $field = $this->updateContactName($field, $args);
388
389             $results['fields'][$i] = $this->prepareField($field, $args);
390         }
391
392         return $results;
393     }
394
395     /**
396      * Returns user search results
397      *
398      * @param string $condition
399      * @return array
400      */
401     protected function getUserResults($condition)
402     {
403         $users = $this->getUserArray($condition);
404
405         $results = array();
406         $results['totalCount'] = count($users);
407         $results['fields']     = array();
408
409         foreach ($users as $id => $name) {
410             array_push(
411                 $results['fields'],
412                 array(
413                     'id' => (string) $id,
414                     'user_name' => $name,
415                     'module' => 'Users'
416             ));
417         }
418
419         return $results;
420     }
421
422     /**
423      * Merges current module search results to given list and returns it
424      *
425      * @param array $data
426      * @param SugarBean $focus
427      * @param string $orderBy
428      * @param string $where
429      * @param string $limit
430      * @param boolean $singleSelect
431      * @return array
432      */
433     protected function updateData($data, $focus, $orderBy, $where, $limit, $singleSelect = false)
434     {
435         $result = $focus->get_list($orderBy, $where, 0, $limit, -1, 0, $singleSelect);
436
437         return array_merge($data, $result['list']);
438     }
439
440     /**
441      * Updates search result with proper contact name
442      *
443      * @param array $result
444      * @param array $args
445      * @return string
446      */
447     protected function updateContactName($result, $args)
448     {
449         global $locale;
450
451         $result[$args['field_list'][0]] = $locale->getLocaleFormattedName(
452             $result['first_name'],
453             $result['last_name'],
454             $result['salutation']
455         );
456
457         return $result;
458     }
459
460     /**
461      * Overrides contact_id and reports_to_id params (to 'id')
462      *
463      * @param array $result
464      * @param object $data
465      * @param array $args
466      * @return array
467      */
468     protected function overrideContactId($result, $data, $args)
469     {
470         foreach ($args['field_list'] as $field) {
471             $result[$field] = (preg_match('/reports_to_id$/s',$field)
472                                || preg_match('/contact_id$/s',$field))
473                 ? $data->id // "reports_to_id" to "id"
474                 : $data->$field;
475         }
476
477         return $result;
478     }
479
480     /**
481      * Returns prepared arguments. Should be redefined in child classes.
482      *
483      * @param array $arguments
484      * @return array
485      */
486     protected function prepareArguments($args)
487     {
488         global $sugar_config;
489
490         // override query limits
491         if ($sugar_config['list_max_entries_per_page'] < ($args['limit'] + 1)) {
492             $sugar_config['list_max_entries_per_page'] = ($args['limit'] + 1);
493         }
494
495         $defaults = array(
496             'whereExtra' => '',
497             'order_by_name' => false,
498             'use_default_where' => true,
499         );
500
501         return array_merge($defaults, $args);
502     }
503
504     /**
505      * Returns prepared field array. Should be redefined in child classes.
506      *
507      * @param array $field
508      * @param array $args
509      * @return array
510      */
511     protected function prepareField($field, $args)
512     {
513         return $field;
514     }
515
516     /**
517      * Returns user array
518      *
519      * @param string $condition
520      * @return array
521      */
522     protected function getUserArray($condition)
523     {
524         return (showFullName())
525             // utils.php, if system is configured to show full name
526             ? getUserArrayFromFullName($condition, true)
527             : get_user_array(false, 'Active', '', false, $condition,' AND portal_only=0 ',false);
528     }
529
530     /**
531      * Returns additional where condition for non private teams
532      *
533      * @param array $args
534      * @return string
535      */
536     protected function getNonPrivateTeamsWhere($args)
537     {
538         global $db;
539
540         $where = sprintf(
541             "(teams.name like '%s%%' or teams.name_2 like '%s%%')",
542             $db->quote($args['conditions'][0]['value']),
543             $db->quote($args['conditions'][0]['value'])
544         );
545
546         $where .= (!empty($args['conditions'][1]) && $args['conditions'][1]['name'] == 'user_id')
547             ? sprintf(
548                 " AND teams.id in (select team_id from team_memberships where user_id = '%s')",
549                 $args['conditions'][1]['value']
550             )
551             : ' AND teams.private = 0';
552
553         return $where;
554     }
555
556     /**
557      * Returns JSON encoded data
558      *
559      * @param array $data
560      * @return string
561      */
562     protected function getJsonEncodedData($data)
563     {
564         $json = getJSONobj();
565
566         return $json->encodeReal($data);
567     }
568
569     /**
570      * Returns formatted JSON encoded search results
571      *
572      * @param array $args
573      * @param array $results
574      * @return string
575      */
576     protected function getFormattedJsonResults($results, $args)
577     {
578         $results = $this->formatResults($results, $args);
579
580         return $this->getJsonEncodedData($results);
581     }
582
583     /**
584      * Returns filtered JSON encoded search results
585      *
586      * @param array $results
587      * @return string
588      */
589     protected function getFilteredJsonResults($results)
590     {
591         $results = $this->filterResults($results);
592
593         return $this->getJsonEncodedData($results);
594     }
595
596     /**
597      * Returns updated arguments array
598      *
599      * @param array $args
600      * @return array
601      */
602     protected function updateQueryArguments($args)
603     {
604         $args['order_by_name'] = true;
605
606         return $args;
607     }
608
609     /**
610      * Returns updated arguments array for contact query
611      *
612      * @param array $args
613      * @return array
614      */
615     protected function updateContactArrayArguments($args)
616     {
617         return $args;
618     }
619
620     /**
621      * Returns updated arguments array for team query
622      *
623      * @param array $args
624      * @return array
625      */
626     protected function updateTeamArrayArguments($args)
627     {
628         $args['whereExtra'] = $this->getNonPrivateTeamsWhere($args);
629         $args['modules'] = array('Teams');
630         $args['use_default_where'] = false;
631
632         return $args;
633     }
634 }